mysql索引失效主因是最左前缀原则被破坏:范围查询或跳过中间列会导致右侧列无法使用索引;order by需满足最左连续列且排序方向一致;索引过多拖慢写入,应评估选择性与实际使用率;explain中key_len和extra比type更能反映索引使用情况。

WHERE 条件里用不到索引?检查最左前缀是否被破坏
MySQL 的 B+ 树索引生效前提是查询能从索引最左侧列开始连续匹配。一旦中间某列用了范围查询(>、>=、BETWEEN、LIKE 前缀不固定),右侧所有列就无法走索引了。
比如有联合索引 INDEX (a, b, c):
-
WHERE a = 1 AND b = 2 AND c = 3→ 全部命中 -
WHERE a = 1 AND b > 2 AND c = 3→ 只用到a和b,c被跳过 -
WHERE a = 1 AND c = 3→ 只用到a,c完全失效(b缺失导致断层)
常见坑:把高频过滤字段放在联合索引右边,或者在中间列加了函数(如 WHERE YEAR(create_time) = 2024),直接让整条索引失效。
ORDER BY 不走索引?确认排序方向和覆盖字段
MySQL 要用索引做排序,必须满足两个条件:排序字段是索引的最左连续列,且所有排序方向一致(全 ASC 或全 DESC)。8.0+ 支持混合方向,但老版本不行。
例如索引 INDEX (user_id, created_at):
-
ORDER BY user_id, created_at→ 可走索引排序 -
ORDER BY user_id DESC, created_at ASC→ 5.7 及以前会触发 filesort -
ORDER BY created_at→ 即使有索引也用不上,因为没包含最左列user_id
额外注意:如果 SELECT * 且索引不是覆盖索引,MySQL 可能宁愿全表扫描 + filesort,也不走索引再回表——这时要权衡是否加 INCLUDE 字段或改写查询。
索引太多反而拖慢写入?评估更新频率和选择性
每多一个索引,INSERT/UPDATE/DELETE 就得多维护一份 B+ 树。尤其对高写入表(如日志、消息队列),索引数量应严格控制。
判断一个索引是否值得保留,看三点:
- 该索引是否被
EXPLAIN显示为key至少 10% 的查询实际使用? - 对应字段的选择性是否足够高?
COUNT(DISTINCT col) / COUNT(*)接近 1 才好(比如user_id),接近 0 的(如status只有 0/1)慎建单列索引 - 有没有更短的等价替代?比如已有
(a, b)索引,再建(a)就冗余
典型误操作:给每个 WHERE 出现过的字段都单独建索引,结果写入变慢 30%,而查询加速微乎其微。
EXPLAIN 显示 type=ALL 或 rows 过大?优先看 key_len 和 Extra
type=ALL 是全表扫描信号,但真正关键的是 key_len(实际用到索引字节数)和 Extra 字段:
-
key_len比预期小 → 说明只用了索引前缀,检查 WHERE 是否漏了左列 -
Extra: Using where; Using index→ 覆盖索引,理想状态 -
Extra: Using filesort或Using temporary→ 排序/分组没走索引,得调结构 -
rows远大于实际返回行数 → 索引统计信息过期,可执行ANALYZE TABLE tbl_name
别只盯着 type 看,key_len = 0 却显示 type=ref 的情况也存在(比如用了函数索引但没触发),得结合 filtered 和实际执行时间判断。
最常被忽略的一点:索引优化不是一锤子买卖。表数据分布变化后(比如某个字段从均匀变成倾斜),原来有效的索引可能突然失效——定期用 pt-query-digest 或慢查日志反查索引使用率,比凭经验猜靠谱得多。











