索引优化策略 - MySQL与PostgreSQL索引设计与优化指南
引言
上周排查某电商订单系统,把高峰期查「7天未发货广州单」的耗时从 4分钟 压到 220ms —— 靠的不是升级 SSD,而是删掉3个重复索引、新增一个精准的复合覆盖索引。
索引是数据库性能的「第一生产力」,但也是一把双刃剑:它能极大加速查询,却会拖慢写入、占用额外的磁盘空间。本文帮你快速掌握 MySQL 和 PostgreSQL 两套主流relational-database的索引核心设计技巧,让索引真正为你的查询模式服务。
1. 索引基础扫盲(快速回顾)
1.1 核心类比
数据库索引就像图书馆的书脊分类 + 按书名/作者的检索目录,不用翻遍所有书架就能快速定位到想要的书。
1.2 四条核心原则
先记牢这四点,后面的所有策略都是它们的延伸:
- 优先为高选择性列建索引
选择性越高(不同值越多),索引效果越好。用户邮箱(几乎唯一)优于订单状态(可能只有3~4种值)。 - 复合索引必须遵循「最左前缀」
索引键按顺序组织,就像目录先按「省份」、再按「城市」排序。跳过省份直接查城市,索引无法生效。 - 写多读少,谨慎建索引
每次写入、更新、删除,所有相关索引的数据都要同步修改,写压力大的表索引过多会变成灾难。 - 定期维护索引健康
删除不再使用的索引,重建碎片严重的索引,保持整体效率。
2. MySQL vs PostgreSQL 核心索引对比
简单记忆:MySQL 的索引生态相对传统但扎实;PostgreSQL 则提供了更丰富、更精准的索引类型,可以应对复杂的半结构化数据和大表场景。
3. 实用索引设计实战(重点!)
3.1 高频查询场景
场景1:复合条件 + 排序 + 覆盖
订单系统的高频查询:
MySQL 方案
因为没有原生的 INCLUDE 子句,我们需要将需要返回的列也包含在复合索引中,实现覆盖索引。
索引键按等值查询列、范围/排序列、覆盖列的顺序排列。这样的索引可以完全覆盖查询,不必回表。
PostgreSQL 方案
使用原生 INCLUDE 子句,将 id、amount 作为非键列包含在索引中,不参与索引搜索逻辑,但可避免回表。
最左前缀验证: 两种方案都能精准利用索引,查询计划中会出现 Using index 或 Index Only Scan。
场景2:只查询某类特定状态的小范围数据
紧抓「近30天未发货的退货审核单」这类只关心一小撮数据的查询。
MySQL 方案
MySQL 不支持原生部分索引,但可以通过函数索引缩小范围(8.0.13+),或者使用普通索引配合范围查询。
PostgreSQL 方案
原生部分索引只对满足 WHERE 条件的行建索引,节省大量空间,且查询自动匹配。
适合只有少量数据满足条件的高频查询,索引体积缩小 90% 以上。
场景3:JSON / 数组查询
MySQL 方案
MySQL 5.7 开始提供 JSON 类型,可通过虚拟列或函数索引优化部分路径查询,但能力较局限。
PostgreSQL 方案
GIN 索引原生支持数组和 JSONB,查询能力强大得多。
查询示例:
如果业务中大量使用 JSON 或数组,PostgreSQL 的优势非常明显。
4. 索引避坑指南
4.1 常见索引失效场景
4.2 三大反模式
- 过度索引:为每个列单独建索引,写操作极度膨胀,存储空间飞涨。
- 键顺序错位:将低选择性列(如 status)放在复合索引最前端,导致索引筛选能力弱。
- 从不维护:大表索引碎片超过 30% 仍不重建,扫描效率可能还不如全表扫描。
5. 快速诊断工具:用 EXPLAIN 看清索引使用
5.1 MySQL EXPLAIN
关键信息:
key:实际被使用的索引名rows:预估扫描行数,越小越好Extra:出现Using index代表覆盖索引,无需回表;Using filesort表示需要额外排序,通常是警告信号
5.2 PostgreSQL EXPLAIN
关键信息:
- 扫描类型优选顺序:Index Only Scan > Index Scan > Bitmap Heap Scan > Seq Scan(全表扫描)
Heap Fetches越少越好,过多说明回表次数高,可考虑扩展覆盖索引
总结
- 按场景选索引,按数据库选特性
普通查询用 B-Tree 覆盖;JSON/数组/全文优先考虑 PG 的 GIN/GiST;时序大表用 BRIN 压缩索引空间。 - 复合索引键顺序有讲究
等值查询列 → 范围/排序列 → 覆盖列(或通过 INCLUDE 单独附加) - 定期巡检慢查询,清理无用索引
用好 EXPLAIN,配合数据库自带的索引统计视图,及时“瘦身”。

