MySQL优化器“突然不认索引”主因是统计信息过期、数据分布突变或隐式转换导致索引失效;5.7默认采样不足且无直方图,8.0+可建直方图优化;需检查持久化统计、手动ANALYZE、统一字符集与校对规则。

MySQL优化器为什么有时“突然不认索引”了
不是优化器变傻了,而是它依赖的统计信息过期、数据分布突变或执行路径成本估算失真。你看到的“偶发选错”,往往发生在统计信息未自动更新、大表批量写入后、或查询条件恰好落在直方图盲区时。
统计信息滞后是最常见原因
MySQL(尤其是5.7)默认只在表变更行数超过 innodb_stats_persistent_sample_pages × 10% 时才触发自动更新;小幅度增删不会刷新统计,导致优化器仍按旧数据分布估算成本。比如一个原本只有几千行的订单表,凌晨批量导入200万行后,EXPLAIN 仍显示走 ref,实际执行却是 ALL。
- 检查是否启用持久化统计:
SHOW VARIABLES LIKE 'innodb_stats_persistent';—— 建议设为ON - 手动刷新(紧急):
ANALYZE TABLE orders;,但注意会加 MDL 读锁,避开高峰期 - 调高采样页数(长期):
SET GLOBAL innodb_stats_persistent_sample_pages = 100;(默认20,太低)
直方图缺失让优化器“看不清”值分布
MySQL 5.7 不支持直方图,8.0+ 才有。没有直方图时,优化器把所有值当成均匀分布——当某字段如 status 中 'pending' 占95%,'shipped' 仅占0.1%,它仍按等概率估算,结果认为全表扫描比索引回表更便宜。
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
- MySQL 5.7 无解,只能靠人工干预:用
FORCE INDEX或重写查询(如拆成UNION) - 升级到 8.0+ 后可建直方图:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status; - 临时绕过:对高频倾斜值单独建覆盖索引,例如
INDEX idx_status_created (status, created_at)
隐式转换+字符集混用直接废掉索引选择
哪怕只有一处 WHERE ref_id = _utf8mb4'xxx' COLLATE utf8mb4_unicode_ci,而列定义是 utf8_unicode_ci,优化器就会放弃该索引——不是它“选错”,而是规则上不允许复用。
- 查当前列 collation:
SHOW CREATE TABLE orders;看ref_id实际使用的COLLATE - 统一写法:去掉显式
COLLATE,改用WHERE ref_id = _utf8mb4'xxx',让 MySQL 自动适配列规则 - 存储过程中尤其要检查
NAME_CONST()或用户变量传入的字符串是否带强制 collation
真正难处理的不是“永远慢”,而是“有时候快有时候慢”——那大概率是统计不准、值分布倾斜或 collation 冲突在暗处作祟。别急着加索引,先确认优化器看到的世界,是不是和你预期的一致。

















