mysql 5.7 升级到 8.0 后执行计划变差主因是统计信息过期(innodb_tablestats last_update 滞后)和优化器行为变更(如 index_merge 默认启用、filesort 策略调整、子查询物化逻辑变化),需通过 analyze table、补直方图、重建复合索引及合理配置 optimizer_switch 来修复。

MySQL 5.7 升级到 8.0 后执行计划变差,不是优化器“退化”,而是它开始认真看数据了——但你没给它看准的数据。
EXPLAIN 的 type 从 ref 变成 ALL 怎么办
这是最直接的信号:优化器误判了索引选择性。5.7 升级到 8.0 后,INNODB_TABLESTATS 表里的 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% 行数的表触发,冷表永远不动 - 立刻执行:
ANALYZE TABLE your_table
为什么 ORDER BY 突然走 Using filesort
8.0 默认启用更激进的内存排序策略,同时废弃了 max_length_for_sort_data,不再按字段长度切分排序模式。原来靠索引有序扫描的查询,现在一律走 filesort,一旦 sort_buffer_size 不够或字段太长,立刻写磁盘临时文件。
动态切换AI模型以优化成本与性能。当用户发出“eco mode”、“balanced mode”、“smart mode”或“max mode”等模式指令,或使用“/modes status”查询状态及“/modes setup”配置模式时触发。
- 判断是否真走索引排序:
EXPLAIN输出中Extra列不含Using filesort才算 - 临时修复:补全复合索引,例如
WHERE a=1 ORDER BY b,c就建INDEX(a,b,c) - 别用
SELECT *加剧恶化——字段越多,filesort内存压力越大;可先试SELECT a,b,c看是否恢复
FORCE INDEX 失效,EXPLAIN 里冒出 using_index_merge
8.0.19+ 默认开启 index_merge=on,优化器会主动把多个单列索引“合并使用”,哪怕你写了 FORCE INDEX (idx_composite),它也可能无视并改走 idx_a,idx_b 合并路径。
- 检查当前开关:
SELECT @@optimizer_switch,确认其中index_merge=on是否启用 - 临时禁用:
SET SESSION optimizer_switch='index_merge=off'; - 注意:
index_merge在多条件 OR 查询中常有收益,盲目关掉可能影响其他语句 - 更稳方案:删掉冗余单列索引,保留覆盖性更强的复合索引
子查询从 DEPENDENT SUBQUERY 变成 MATERIALIZED 但反而更慢
8.0 默认启用 subquery_materialization_cost_based=ON,会尝试物化子查询。但若子查询含 LIMIT 或 GROUP BY,又会退回到物化临时表——而你从 SQL 看不出这个切换逻辑。
- 确认是否真物化:
EXPLAIN FORMAT=TREE中出现materialize节点才算 - 若物化后
rows估算远低于实际(比如预估 1 行,实际 10 万行),说明直方图缺失或统计不准,ANALYZE TABLE后仍需补直方图 - 含
LIMIT的IN子查询在 8.0 中大概率退化,改用JOIN或显式STRAIGHT_JOIN更可控 -
SELECT列表里的标量子查询(如SELECT (SELECT name FROM users WHERE id = o.user_id))仍不会自动物化,必须手动改写为LATERAL或派生表
真正要盯住的不是版本号,而是 EXPLAIN FORMAT=TREE 里有没有 materialize、Hash Aggregate 这类节点,以及它们的 rows 估算是否合理——否则提示再好,优化器也会绕开它。










