MySQL升级后EXPLAIN执行计划变差,主因是新版本统计信息未自动更新导致优化器误判索引选择性;需对核心表手动ANALYZE TABLE刷新统计信息,并注意避免高峰操作。

为什么MySQL升级后EXPLAIN显示的执行计划变差了
MySQL升级(尤其是5.7→8.0或8.0.x小版本间)会默认启用新的**统计信息采样算法**和**索引基数估算逻辑**,旧表的table_stats和index_stats不会自动刷新。优化器基于过时的统计信息误判索引选择性,可能跳过本该用的索引,转而走全表扫描或错误的连接顺序。
- 典型现象:
EXPLAIN里type从ref退化成ALL,rows预估暴涨几倍甚至几十倍 - 不是所有表都出问题——只影响升级前长期未
ANALYZE TABLE、且数据分布不均(如时间字段有大量NULL或倾斜值)的表 - 8.0.19+默认开启
innodb_stats_auto_recalc=ON,但仅对“数据变更超10%”的表触发,冷表永远不更新
ANALYZE TABLE要加PERSISTENT FOR ALL吗
不用。MySQL 8.0的ANALYZE TABLE默认已采集持久化统计信息(存入mysql.innodb_table_stats),加PERSISTENT FOR ALL是5.6时代的遗留语法,8.0已废弃,执行会报错ERROR 1064。
- 正确做法就是直接运行
ANALYZE TABLE your_table_name - 如果表很大,加
WITH SYNC(如ANALYZE TABLE t1 WITH SYNC)可强制同步更新,避免后台异步任务延迟 - 批量处理时别用
SELECT table_name FROM information_schema.tables拼SQL——注意过滤掉information_schema、mysql等系统库的表,否则会卡住
升级后FORCE INDEX突然失效是怎么回事
8.0.19起引入了更激进的**索引合并优化(Index Merge Optimization)**,当优化器认为多个单列索引组合比强制指定的复合索引更快时,会忽略FORCE INDEX。这不是bug,是优化器逻辑变更。
- 验证方法:在
EXPLAIN FORMAT=JSON输出里搜"using_index_merge"字段 - 临时绕过:加
IGNORE INDEX禁用掉它想合并的那些单列索引,例如SELECT * FROM t WHERE a=1 AND b=2 IGNORE INDEX (idx_a,idx_b) FORCE INDEX (idx_a_b) - 根本解决:删掉冗余单列索引(如已有
(a,b)就别留(a)),减少优化器的“错误联想”
哪些表必须优先ANALYZE,哪些可以先放一放
优先处理被慢查询日志反复抓到、且EXPLAIN里rows远大于实际结果行数的表;对纯写入、几乎不查的表(如日志表)可暂缓。
- 必做:
JOIN中的驱动表、WHERE条件含高选择性字段(如用户ID、订单号)的主表 - 可缓:
ORDER BY ... LIMIT且结果集恒定小的表(如配置表)、ENUM或SET字段占主导的表(统计信息影响小) - 危险操作:别在业务高峰跑
ANALYZE TABLE——会锁表(InnoDB下是MDL读锁,但并发DML多时易阻塞)
统计信息不是一劳永逸的事。8.0之后的自动采样阈值、直方图支持(需手动ANALYZE TABLE ... UPDATE HISTOGRAM)、以及不同存储引擎的差异,会让同一套操作在不同环境效果不一。最稳妥的做法,是在升级后的第一个维护窗口,对核心表逐个EXPLAIN对比,而不是依赖一次批量ANALYZE就以为万事大吉。


















