MySQL 8.0升级后查询变慢主因是优化器依赖的统计信息未自动更新及optimizer_switch默认值变更,导致索引选择性误判、filesort滥用、index_merge干扰和慢日志解析异常。

不是所有查询都会变慢,但变慢的几乎都和优化器“重新认识数据”有关——MySQL 8.0 的优化器更依赖统计信息、更激进启用新算法,而升级后旧表的统计信息不会自动刷新,optimizer_switch 默认值也变了。
为什么 EXPLAIN 显示 rows 暴涨、type 从 ref 变成 ALL
这是最典型的信号:优化器误判了索引选择性。5.7 升级到 8.0 后,INNODB_TABLESTATS 和 INNODB_INDEXSTATS 中的 last_update 时间仍停留在升级前,导致优化器看到的行数估算(rows)严重偏离真实值。
- 执行
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESTATS WHERE TABLE_NAME = 'your_table',检查last_update是否早于升级时间 - 用
SHOW INDEX FROM your_table查Cardinality,再对比SELECT COUNT(DISTINCT your_col) FROM your_table,若差 10 倍以上,基本确认失真 - 别等自动更新——
innodb_stats_auto_recalc=ON只对“单次变更超 10% 行数”的表触发,冷表永远不动
ORDER BY 突然变慢,且 EXPLAIN 出现 Using filesort
8.0 默认启用更激进的内存排序策略,同时废弃了 max_length_for_sort_data 参数,不再按字段长度切分排序模式。结果就是:原来靠索引有序扫描的查询,现在一律走 filesort,一旦 sort_buffer_size 不够或字段太长,立刻写磁盘临时文件。
- 检查是否命中索引排序:
EXPLAIN输出中Extra列不含Using filesort才算真正走索引有序扫描 - 临时修复:给排序字段补全复合索引,例如
WHERE a=1 ORDER BY b,c就建INDEX(a,b,c) - 避免
SELECT *加剧恶化——字段越多,filesort内存压力越大;可先试SELECT a,b,c看是否恢复毫秒级
FORCE INDEX 失效,执行计划里冒出 using_index_merge
8.0.19+ 默认开启 index_merge=on,优化器会主动把多个单列索引“合并使用”,哪怕你写了 FORCE INDEX (idx_composite),它也可能无视并改走 idx_a,idx_b 合并路径。
- 在
EXPLAIN FORMAT=TREE或EXPLAIN ANALYZE输出里搜using_index_merge字段即可确认 - 临时绕过:加
IGNORE INDEX (idx_a, idx_b)把它想合并的单列索引全禁掉 - 根本解法:删掉冗余单列索引——比如已有
(a,b)复合索引,就别留(a)或(b)单列索引,减少优化器的“错误联想”
慢查询日志突然“变少”或解析失败
这不是性能变好,而是日志行为变了:8.0 默认只记录已提交事务的慢语句,且时间戳精度升到微秒,老版分析工具(如 mysqldumpslow)直接无法识别。
- 查当前配置:
SELECT @@slow_query_log, @@log_output, @@long_query_time,确认日志是否真的开着、输出到哪 - 若用文件日志,必须显式设
SET GLOBAL log_output = 'FILE'并指定slow_query_log_file - 用
pt-query-digest分析前,先跑pt-query-digest --print --no-report /path/to/slow.log | head -10,看时间字段是否能正常解析为数值
最容易被忽略的是:统计信息更新本身会加 MDL 读锁,高并发 DML 场景下容易阻塞业务;别在流量高峰跑 ANALYZE TABLE,优先挑慢查询日志里反复出现的驱动表下手。











