先给结论
12 种:索引列上套函数、索引列参与运算、隐式类型转换、字符集或排序规则不一致、前导通配符、违反最左前缀、范围条件之后的列、OR 连了非索引列、不等类条件、区分度过低、统计信息过期、排序分组与索引不匹配。
它们分成两大类:前八种是写法让索引\”用不上\”,后四种是优化器算完成本主动放弃。这个分法比背清单有用。
一个典型的排查现场
很多团队上线前都会遇到这类事:一张订单表,status 和 create_time 都建了索引,某个查询却慢得离谱,慢日志里平均 3.6 秒。
翻代码,SQL 长这样:
SELECT order_no, amount FROM t_order
WHERE DATE(create_time) = \’2026-10-01\’
AND status = 2;
create_time 上明明有索引。加 EXPLAIN 一看,type 是 ALL,key 是 NULL,rows 估算四百多万,确实没走。
改成范围写法:
SELECT order_no, amount FROM t_order
WHERE create_time >= \’2026-10-01 00:00:00\’
AND create_time < \’2026-10-02 00:00:00\’
AND status = 2;
再 EXPLAIN,type 变成 range,rows 掉到几千,实测从 3.6 秒降到 0.02 秒量级。数字以你自己的环境为准,量级差是真实的。
差别只有一处:DATE() 这个函数被去掉了。
先搞清楚\”失效\”到底是什么
这是全篇最重要的一句:索引失效不是索引坏了,是优化器算了成本之后决定不用它。
MySQL 优化器基于成本估算选路径。走二级索引要回表,回表是随机 IO;全表扫描是顺序 IO,读得多但一次能读一整个区。估算命中行数超过全表某个比例时,它会认为顺序扫更快,于是放弃索引。
这个比例不是固定值,常见说法在 20% 到 30% 区间,还受统计信息和页聚簇程度影响,别当硬阈值背下来。
排查顺序:先看 EXPLAIN,确认优化器选了什么;再判断是\”用不上\”还是\”不想用\”。前者改 SQL,后者改数据分布或用 FORCE INDEX 兜底。
EXPLAIN 四个字段够用了:
- type:从坏到好是 ALL、index、range、ref



