MySQL 5.7升级到8.0后查询变慢主因是Cost Model重估导致执行计划劣化,根源在于统计信息陈旧或缺失直方图,使优化器低估排序/JOIN成本而弃用索引;需及时ANALYZE TABLE并补充直方图,或针对性关闭hash_join、skip_scan等新特性验证。

MySQL 5.7升级到8.0后查询总耗时增加,大概率是Cost Model重估导致执行计划劣化,不是“变慢了”,而是“选错了”。
为什么EXPLAIN显示的rows没变但实际变慢了
MySQL 8.0默认启用新的cost_model,它不再依赖固定经验值估算IO/CPU开销,而是基于表统计信息(如页数、索引基数、数据分布直方图)动态计算。如果表未收集直方图或统计信息陈旧,优化器可能严重低估排序/临时表/JOIN的成本,从而放弃走索引、改用全表扫描+Using filesort。
- 检查是否启用直方图:
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE SCHEMA_NAME = 'db_name' AND TABLE_NAME = 'tbl_name'; - 强制更新统计信息:
ANALYZE TABLE tbl_name UPDATE HISTOGRAM ON col1, col2;(仅8.0.19+支持) - 临时禁用cost model验证:
SET SESSION use_secondary_engine=OFF;或回退到传统估算:SET SESSION optimizer_use_condition_selectivity=1;
ORDER BY没索引时性能断崖式下跌
MySQL 5.7对无索引ORDER BY会尽量复用已扫描行的内存排序(Creating sort index阶段),而8.0在cost model下更倾向认为“先扫再排”比“边扫边排”更优——结果就是大量数据被读入临时表再排序,I/O和内存压力骤增。
- 典型症状:
EXPLAIN中出现Using temporary; Using filesort,且rows远小于filtered值 - 快速验证:对慢SQL加
FORCE INDEX或显式ORDER BY ... LIMIT 10,看是否恢复毫秒级响应 - 根治方法:补上
ORDER BY字段的联合索引,例如WHERE a=1 ORDER BY b,c→INDEX(a,b,c)
optimizer_switch默认值变更引发隐性退化
8.0默认开启hash_join=on和skip_scan=on,看似利好,但在小表JOIN大表、或存在NULL值的字段上,hash join可能因哈希桶冲突退化为O(n²),skip scan在低基数列上反而比range scan更耗资源。
- 查当前设置:
SELECT @@optimizer_switch\G - 针对性关闭测试:
SET SESSION optimizer_switch='hash_join=off,skip_scan=off'; - 注意:某些场景下
materialization=on也会让子查询从“一次执行”变成“多次物化”,需结合EXPLAIN FORMAT=TREE观察嵌套层级
Cost Model不是黑盒,它的输出高度依赖统计信息新鲜度和数据分布真实性。升级后不跑ANALYZE TABLE,等于让优化器蒙眼开车——哪怕引擎本身快了一倍,路线选错照样绕远路。最易被忽略的是:直方图只对VARCHAR等非均匀分布字段有效,而INT主键列即使有直方图也几乎不影响成本判断。


















