查执行计划差异应优先使用explain format=tree和cost_info:5.7升8.0后慢查主因常是优化器路径变更,如从索引扫描变为全表扫描+排序;需对比嵌套结构与query_cost/sort_cost是否飙升,并验证统计信息是否失效、optimizer_switch默认值变化及persist配置覆盖问题。

查执行计划差异:重点看EXPLAIN FORMAT=TREE和cost_info
5.7 升 8.0 后慢查,第一反应不该是“加索引”,而是确认优化器是否选了完全不同的路径。8.0 的 EXPLAIN 增加了 FORMAT=TREE 和 cost_info 字段,能直接暴露代价估算偏差。
- 用
EXPLAIN FORMAT=TREE对比升级前后同一 SQL 的嵌套结构:若 5.7 是 “Index range scan → Filter”,而 8.0 变成 “Table scan → Sort → Limit”,说明优化器弃用了索引 -
EXPLAIN FORMAT=JSON中的cost_info字段里,重点关注query_cost和sort_cost是否异常飙升(比如从 120 跳到 12000) - 别只信
type: index—— 若rows显示 100 但实际扫描 50 万行,大概率是统计信息过期,不是执行计划本身错
验证统计信息是否失效:INFORMATION_SCHEMA.INNODB_TABLESTATS和ANALYZE TABLE
5.7 升级后 INNODB_TABLESTATS.last_update 时间仍停留在升级前,会导致优化器对行数严重误判。这不是“没收集”,是升级过程没触发自动刷新。
- 查失真:运行
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 默认就是 ON,但老配置可能覆盖),并确认innodb_stats_auto_recalc = ON
检查optimizer_switch默认值变更:特别是derived_merge、hash_join和mrr
8.0 至少有 12 项 optimizer_switch 子开关默认值变了,其中三个最常引发退化: derived_merge=on(让子查询合并,但常导致索引失效)、hash_join=on(JOIN 性能提升明显,但对小结果集反而更慢)、mrr=off(5.7 是 on,关闭后范围扫描效率下降)。
- 导出 baseline:升级前务必存档
SELECT @@optimizer_switch;,升级后逐项比对 - 临时验证:在慢查前加
SET SESSION optimizer_switch='derived_merge=off,hash_join=off,mrr=on';,再跑EXPLAIN看是否回归原路径 - 若
Extra出现Using intersect()或Using union(),基本可锁定index_merge=on在捣乱,先关掉试试
排查PERSIST机制是否覆盖了你的my.cnf
8.0 的 SET PERSIST 会把变量写入 mysqld-auto.cnf,且该文件优先级高于 my.cnf。很多团队升级后改了配置却无效,就是因为旧版遗留的持久化设置还在生效。
- 查当前真实生效值:
SELECT VARIABLE_NAME, VARIABLE_VALUE 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来自PERSISTED,而你期望它来自CONFIG,需执行RESET PERSIST sort_buffer_size;再重启
真正容易被忽略的是:8.0 的排序策略已彻底废弃 max_length_for_sort_data,sort_buffer_size 不够时不再降级为 rowid 排序,而是直接走磁盘 filesort —— 这个行为切换无声无息,但会让原本 1 秒的查询变成 4 秒,且只在 SELECT * 时爆发。











