mysql升级后sql变慢主因是优化器统计信息失效,导致执行计划错误;需用analyze table更新统计信息,并检查optimizer_switch等配置变更。

MySQL 升级后 SQL 执行计划变慢,大概率不是 SQL 本身的问题,而是优化器对数据分布的“认知”失效了 —— 统计信息没更新,优化器误判成本,选了更差的索引或连接顺序。
为什么升级后统计信息会失效
MySQL 升级(尤其是跨大版本,如 5.7 → 8.0)会重置或忽略旧版本生成的统计信息。InnoDB 的统计信息(如 INFORMATION_SCHEMA.INNODB_TABLESTATS、INFORMATION_SCHEMA.INNODB_INDEXSTATS)并非实时持久化,且 8.0 默认启用 innodb_stats_persistent = ON,但升级时不会自动重建。结果就是:优化器看到的行数估算(rows 字段)、索引基数(cardinality)严重偏离真实值,导致 EXPLAIN 中 type 从 ref 退化为 ALL,或错误跳过覆盖索引。
怎么确认是统计信息问题
别猜,直接查:
- 执行
SHOW INDEX FROM table_name,对比Cardinality列和实际唯一值数量(可用SELECT COUNT(DISTINCT col) FROM table_name验证); - 用
EXPLAIN看慢 SQL,重点看rows估算值是否离谱(比如表只有 10 万行,却显示rows=12000000); - 检查
INFORMATION_SCHEMA.INNODB_TABLESTATS中last_update时间是否早于升级时间; - 对比升级前后同一条 SQL 的
EXPLAIN输出,若key、type、rows显著变化,且无 DDL 变更,基本可锁定。
重新收集统计信息的实操方式
优先用 ANALYZE TABLE,它安全、轻量、立即生效:
- 单表刷新:
ANALYZE TABLE orders;(会锁表读,但不阻塞写,通常秒级); - 批量刷新(推荐):
SELECT CONCAT('ANALYZE TABLE ', table_schema, '.', table_name, ';') FROM information_schema.tables WHERE table_schema = 'your_db' AND table_rows > 10000;,拼出语句后批量执行; - 避免用
mysql_upgrade或重启来“触发”统计——它不负责这个,纯属误导; - 不要依赖
innodb_stats_auto_recalc = ON自动更新:它只在表变更超 10% 时触发,且首次升级后不会补算,必须手动干预。
升级后必须同步检查的配置项
统计信息只是起点,升级常伴随默认行为变更:
-
optimizer_switch:8.0 默认启用更多优化规则(如hash_join=on),但某些老 SQL 可能因 join 算法切换变慢,用SELECT @@optimizer_switch;对比升级前后; -
join_buffer_size:8.0 默认值可能更小,大 join 容易退化为 Block Nested-Loop,需按内存调高; -
innodb_stats_persistent_sample_pages:控制采样页数,默认 20,数据倾斜严重时建议调到 100,让统计更准; - 注意
SQL_MODE变更(如严格模式开启),可能让原本隐式转换的查询报错或走不同执行路径。
统计信息更新后,EXPLAIN 的 rows 和 key 通常立刻回归合理范围。但真正容易被忽略的是:升级后优化器成本模型变了,即使统计准确,它也可能因新规则放弃你习惯的索引——这时候得看 EXPLAIN FORMAT=TREE,而不是只盯传统格式。











