MySQL 8.0索引“失效”主因是优化器校验更严格:ORDER BY方向、联合索引字段顺序、COLLATION或类型不完全对齐时即跳过索引;EXPLAIN中key非空但Extra含Using filesort即表明索引被用于查找却未用于排序。

不是索引“变差了”,而是 MySQL 8.0 的优化器在某些复合索引场景下更严格地校验使用条件,稍有不匹配就跳过索引——尤其当索引字段顺序、排序方向、类型或 COLLATION 不完全对齐时。
ORDER BY 多字段方向不一致触发 filesort
MySQL 5.7 对 DESC 索引字段视而不见,建 INDEX(a DESC, b ASC) 实际存成 (a ASC, b ASC);它靠后向扫描勉强撑住降序查询。但 MySQL 8.0 真正支持降序索引,反而要求查询的 ORDER BY 方向必须与索引定义严格一致。
- 若索引是
INDEX(user_id ASC, created_at DESC),而查询写ORDER BY user_id DESC, created_at ASC,8.0 直接放弃该索引,走Using filesort - 5.7 可能还凑合用(靠反向扫),8.0 则明确判定“方向冲突”,不尝试兼容
- 验证方式:
EXPLAIN中Extra出现Using filesort,且key列非空但未生效
联合索引字段顺序与 WHERE + ORDER BY 混用失配
MySQL 8.0 更激进地依赖“最左前缀 + 排序连续性”来决定是否复用索引做排序。哪怕 WHERE 条件能走索引,只要 ORDER BY 字段不在 WHERE 覆盖的最右连续段上,就可能拒绝索引排序。
- 例如索引
INDEX(status, category, updated_at),查询WHERE status = ? AND category = ? ORDER BY updated_at DESC在 8.0 中可免排序;但若写成ORDER BY status, updated_at,即使status是索引首列,也会因“非连续段”失效 - 5.7 有时会退化为部分利用(如只用前两列查数据,再 filesort),8.0 更倾向于全弃或强制走索引覆盖
- 修复关键:把
ORDER BY涉及的字段尽量放在索引右侧连续位置,避免中间穿插非排序字段
COLLATION 不一致导致隐式 CONVERT 阻断索引
这是升级后最隐蔽的性能断崖点:字段是 utf8mb4_general_ci,但连接默认 utf8mb4_0900_ai_ci,MySQL 8.0 会在比较前自动加 CONVERT(col USING utf8mb4_0900_ai_ci) ——只要出现这个函数,B+ 树索引立即失效。
- 常见于 JOIN 场景:两张表字段都是
VARCHAR,但 COLLATION 不同,ON t1.name = t2.name就触发隐式转换 -
EXPLAIN中type降为ALL或key为NULL,但SHOW FULL COLUMNS显示字段明明有索引 - 快速确认:
SELECT @@collation_connection, (SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='t' AND COLUMN_NAME='col')对比是否一致
optimizer_switch 中 index_merge 被误启用
MySQL 8.0 默认开启 index_merge=on,它会让优化器尝试合并多个单列索引。听起来好,但实际常导致更差的执行计划:两个索引各自返回 10 万行,取交集后只剩 10 行,中间过程却放大 I/O 和内存压力。
- 现象:
EXPLAIN中Extra出现Using intersect(...)或Using union(...),且rows预估远高于实际结果集 - 对比 5.7:它默认不用 index_merge,老查询反而稳定走一个靠谱的复合索引
- 临时验证:
SET SESSION optimizer_switch='index_merge=off';后重跑EXPLAIN,若key变成你预期的复合索引名,就是它了
真正容易被忽略的是:这些退化往往只发生在特定参数组合或 JOIN 顺序下,单表测试一切正常,一进业务 SQL 就崩——别急着加索引,先抓一条真实慢查询的 EXPLAIN FORMAT=TREE,盯住 key、type、Extra 和 rows 四个字段,它们比任何经验都诚实。



















