索引并非越多越好,关键在于实现“只走索引、不回表”并降低维护开销;应优先满足高频查询的过滤与排序,通过覆盖索引避免回表,并用组合索引替代冗余单列索引。

索引不是越多越好,关键在于让查询“只走索引、不回表”,同时减少索引维护开销。核心思路是:优先满足高频查询的过滤与排序需求,再通过覆盖索引避免回表,最后用组合索引合并多个单列索引。
什么是索引覆盖?为什么它快?
当一条查询所需的所有字段,全部被某个索引的键列(包括主键隐含列)包含时,MySQL 就能直接从索引B+树的叶子节点读出全部数据,无需再回主键索引查找行记录——这叫“索引覆盖”。它省去了随机IO,大幅降低查询延迟。
例如:user表有联合索引 (status, create_time, id),执行:SELECT id, status, create_time FROM user WHERE status = 'active' ORDER BY create_time LIMIT 10;
这个查询完全命中索引,不需要回表,就是典型的覆盖查询。
注意:SELECT * 几乎不可能走覆盖索引;TEXT/BLOB列无法被普通索引存储,也会破坏覆盖效果。
组合索引设计的三个关键原则
组合索引顺序直接影响能否命中和是否覆盖,需按“过滤 > 排序 > 覆盖”优先级排列:
- 等值条件列放最左:如 WHERE a = ? AND b = ?,索引 (a,b) 可用,(b,a) 则 a 无法走索引
- 范围查询列放最后:WHERE a = ? AND b > ? AND c = ?,索引应为 (a,c,b),而非 (a,b,c),否则 c 无法利用索引
- 把 SELECT 字段补在右侧:为实现覆盖,在满足过滤/排序前提下,把常用查询字段追加到索引末尾(如 (a,b,created_at,name,email))
如何识别并替换低效单列索引?
存在多个单列索引(如 idx_a、idx_b、idx_c)时,若常有查询同时用到其中两列或三列,大概率可以合并。方法如下:
- 查
SHOW INDEX FROM table_name,对比各索引的 Cardinality 和使用频率(可通过 performance_schema 或慢日志分析) - 用
EXPLAIN看实际查询是否走了多个单列索引(type=index_merge 表示用了索引合并,性能通常不如单个组合索引) - 删除冗余单列索引,新建组合索引,并确保新索引能覆盖原查询的 WHERE + ORDER BY + SELECT 字段
例如:已有 idx_status、idx_create_time、idx_user_id,而常见查询是 WHERE status='paid' AND user_id=123 ORDER BY create_time DESC,则建 (status,user_id,create_time) 更高效。
需要警惕的“伪覆盖”陷阱
看起来走了索引,但实际仍回表,常见于:
- 使用了函数或表达式:WHERE YEAR(create_time) = 2024 → 无法用 create_time 索引
- 隐式类型转换:字符串字段存数字(如 order_no VARCHAR),却用
WHERE order_no = 123→ 索引失效 - LIKE 左模糊:
WHERE name LIKE '%abc'无法利用索引前缀 - OR 连接不同字段:
WHERE a=1 OR b=2,除非都建了索引且优化器选了 index_merge,否则容易全表扫
这些情况需改写SQL或调整字段类型,不能仅靠加索引解决。









