执行计划变慢主因是MySQL 8.0优化器升级后统计信息陈旧、optimizer_switch默认启用新规则(如hash_join、derived_merge)、排序成本模型更严格,三者叠加导致索引误选、rows暴增、filesort频发。

执行计划变慢,八成是优化器“换脑子”但没同步更新数据认知——统计信息陈旧、optimizer_switch默认值变了、排序逻辑更较真了,三者叠加让同一条SQL在8.0里选错索引、多扫几十万行、甚至硬扛Using filesort。
查EXPLAIN里type和rows是否突变
升级前后跑同一句SQL的EXPLAIN,重点盯这两个字段:
-
type从ref变成ALL:不是SQL写错了,是优化器误判索引选择性,大概率统计信息失效 -
rows预估暴涨几倍甚至几十倍:比如表实际10万行,EXPLAIN显示rows=12000000,基本可断定INNODB_TABLESTATS.last_update还卡在升级前 -
Extra里突然多出Using filesort或Using temporary:8.0的cost_model对排序更敏感,ORDER BY没走索引时,它宁可全量读再排,也不愿用老版本的“偷懒”策略
确认INNODB_TABLESTATS.last_update是否过期
别等innodb_stats_auto_recalc=ON自动触发——它只对单次变更超10%行数的表生效,冷表、配置表、归档表永远不动。
- 查更新时间:
SELECT last_update FROM INFORMATION_SCHEMA.INNODB_TABLESTATS WHERE TABLE_NAME = 'your_table',若早于升级时间,就是它 - 比基数失真度:
SHOW INDEX FROM your_table里的Cardinality值,和SELECT COUNT(DISTINCT col) FROM your_table差10倍以上,说明统计严重偏离 - 刷新动作要快:
ANALYZE TABLE t1 WITH SYNC强制同步更新,避免后台异步延迟;别加PERSISTENT FOR ALL,8.0已废弃该语法,会报ERROR 1064
检查optimizer_switch哪些开关被默认启用
5.7→8.0升级后,SELECT @@optimizer_switch输出里多了几个=on项,它们不是增强,而是“新规则上线”:
-
hash_join=on:大表JOIN时可能更快,但若join_buffer_size太小或关联字段无索引,反而退化成多次扫描 -
derived_merge=on:子查询自动合并进外层,但会破坏原有索引利用路径(比如子查询有ORDER BY LIMIT,合并后无法下推) -
skip_scan=on:对低基数字段本意是加速,但统计不准时容易误判,导致跳过更优复合索引 - 临时验证方法:
SET SESSION optimizer_switch='hash_join=off,derived_merge=off';再跑EXPLAIN,看执行计划是否回归。别全局关,只改关键项
留意ORDER BY和视图背后的真开销
8.0.20+彻底废弃max_length_for_sort_data,SELECT * + ORDER BY不再按“行ID排序”偷懒,而是实打实算内存开销——字段越多,越容易触发磁盘filesort。
- 视图查询变慢,往往不是视图本身问题。直接
EXPLAIN视图只会看到type=derived,必须用SHOW CREATE VIEW拿到原始SQL再分析 - 依赖
performance_schema的视图(如sys.innodb_lock_waits)在8.0中底层切到data_lock_waits,数据量可能暴涨几个数量级,每次查询都扫全表+抢trx_sys->mutex -
FORCE INDEX突然失效?8.0.19+引入更激进的index_merge,优化器可能认为多个单列索引组合比你指定的复合索引更快——搜EXPLAIN FORMAT=TREE输出里的using_index_merge就能确认
真正难处理的不是单个参数或开关,而是统计信息、索引设计、内存配置、优化器规则这四者拧在一起。关一个optimizer_switch可能缓解症状,但不更新INNODB_TABLESTATS,下一次查询照样翻车。


















