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

不是索引“失效”了,而是 MySQL 8.0 的优化器更较真了——它会严格比对索引定义和查询使用方式的每一个细节,稍有不匹配就弃用索引,转而走全表扫描或 filesort。
EXPLAIN 显示 key 非空但 Extra 含 Using filesort
这说明索引被用于 WHERE 条件查找,却没被用于 ORDER BY 排序,本质是排序字段与索引结构不连续或方向不一致。
- 典型场景:索引是
INDEX(status, category, updated_at),但查询写WHERE status = ? ORDER BY status, updated_at——中间缺了category,8.0 不认为这是“连续排序段”,直接放弃索引排序 - 降序索引陷阱:5.7 忽略
DESC关键字,建INDEX(a DESC, b ASC)实际存成全升序;8.0 真正支持物理降序存储,但要求ORDER BY必须严格匹配方向,比如索引是a DESC, b ASC,就不能用ORDER BY a DESC, b DESC - 验证方法:对比升级前后
EXPLAIN FORMAT=TREE输出,看排序节点是否从 “Index ordered scan” 变成了 “Sort → Limit”
统计信息过期导致优化器误判索引成本
5.7 升级到 8.0 后,INFORMATION_SCHEMA.INNODB_TABLESTATS.last_update 时间通常不会自动更新,优化器仍按旧数据估算行数,可能把实际 100 行的表当成 10 万行,于是判定“走索引不如全表扫”。
- 快速检查:
SELECT TABLE_NAME, N_ROWS FROM INFORMATION_SCHEMA.INNODB_TABLESTATS WHERE TABLE_NAME = 'your_table';,再对比SELECT COUNT(*) FROM your_table;,差 10 倍以上基本可确认失真 - 立刻修复:
ANALYZE TABLE your_table;(注意不是UPDATE HISTOGRAM) - 长期防复发:确保
innodb_stats_persistent = ON(8.0 默认开启)且innodb_stats_auto_recalc = ON
optimizer_switch 默认值变更干扰执行路径
MySQL 8.0 至少有 12 项 optimizer_switch 子开关默认值变了,其中三个最常引发退化:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
derived_merge=on:合并子查询时可能破坏原有索引利用路径 -
hash_join=on:对小结果集 JOIN 反而比 Nested Loop 慢 -
mrr=off(5.7 是on):关闭后范围扫描效率下降,尤其影响IN或多条件范围查询 - 临时验证:
SET SESSION optimizer_switch='derived_merge=off,hash_join=off,mrr=on';,再跑EXPLAIN看是否回归原路径 - 若
Extra出现Using intersect()或Using union(),大概率是index_merge=on在拼凑单列索引,先关掉试试
PERSIST 机制让旧配置“悄悄失效”
你改了 my.cnf 却没生效?很可能是 mysqld-auto.cnf 里的持久化变量在起作用——它优先级高于配置文件,且重启后自动加载。
- 查当前生效值:
SELECT * FROM performance_schema.persisted_variables; - 看加载顺序:
SELECT VARIABLE_NAME, VARIABLE_SOURCE, VARIABLE_PATH FROM performance_schema.variables_info WHERE VARIABLE_SOURCE IN ('PERSISTED', 'CONFIG'); - 关键变量如
sort_buffer_size、optimizer_switch、innodb_buffer_pool_size都可能被锁死,不清理 PERSIST 就永远调不动 - 测试环境可临时清空:
RESET PERSIST;,再重启验证
真正难处理的从来不是“加不加索引”,而是当 EXPLAIN 显示用了索引、key 非空、rows 却严重失真时,你得知道该去查 INNODB_TABLESTATS,而不是盯着 SQL 改写。这些细节不显眼,但决定慢查能不能在十分钟内定位清楚。










