mysql 8.0执行计划突变主因是默认启用hash_join等新优化规则且统计信息未更新,导致type降级、rows误估;无原生spm,需靠optimizer_switch控制或hint干预,排查须按explain→开关比对→统计信息刷新顺序执行。

执行计划突变不是MySQL“抽风”,而是8.0优化器默认启用了新规则(如hash_join=on、derived_merge=on)且统计信息未刷新——这两者叠加,直接导致EXPLAIN里type从ref跳成ALL、rows预估暴涨几倍。SPM(SQL Plan Management)在MySQL中并不存在原生实现,所谓“绑定执行计划”实际靠optimizer_switch临时控制或USE_INDEX/IGNORE_INDEX提示干预,不是Oracle那种真正的SPM。
怎么确认是optimizer_switch开关惹的祸
别靠经验猜。升级后某条SQL突然变慢,第一件事是比对新旧库的开关状态:
- 在旧库(5.7)和新库(8.0)分别执行
SELECT @@optimizer_switch;,把输出粘贴并排对比 - 重点关注新增的
=on项:比如hash_join=on、derived_merge=on、mrr=on、use_index_extensions=on - 若该SQL涉及JOIN且
EXPLAIN FORMAT=JSON里出现"chosen_plan": "hash_join",而join_buffer_size又偏小(默认128KB),基本就是它在内存不足时退化成多次扫描 - 子查询变慢?检查
derived_merge=on是否把ORDER BY ... LIMIT子句合并进外层,导致索引无法下推
ANALYZE TABLE不光是“刷一下”,得看刷对没
统计信息过期是执行计划劣化的底层原因,但ANALYZE TABLE不是万能膏药,用错方式反而掩盖问题:
- 只对高频查询表执行,比如被慢日志反复抓到、
EXPLAIN中rows远大于实际结果行数的表;日志表、配置表可暂缓 - 大表建议加
WITH SYNC:例如ANALYZE TABLE orders WITH SYNC;,避免后台异步采样延迟导致计划迟迟不更新 - 别用
mysqlcheck --analyze --all-databases——会锁住系统库(如mysql、information_schema),可能卡死权限校验 - 检查
INFORMATION_SCHEMA.INNODB_TABLESTATS里的last_update时间,必须晚于升级时间才算生效
没有SPM,但可以用Hint临时稳住关键SQL
MySQL没有SQL Plan Baseline机制,但可通过优化器提示强制走老路径,适合生产救急:
- 优先用
USE_INDEX:例如SELECT /*+ USE_INDEX(t idx_user_id_status) */ * FROM users t WHERE user_id = 123 AND status = 1; - 禁用干扰项比启用更有效:比如发现
index_merge误判,就加IGNORE INDEX (idx_a,idx_b),再配合FORCE INDEX (idx_a_b) - Hint只影响单条语句,不会污染其他查询;但别长期依赖——应用重启、连接池复用后失效,且无法覆盖存储过程内部SQL
- 注意
USE_INDEX不等于“一定走”,如果索引列存在隐式转换(如字段是utf8mb4_0900_ai_ci,传参是utf8mb4_general_ci),Hint也会被无视
真正难处理的从来不是某个开关或Hint,而是optimizer_switch、统计信息、字符集排序规则、join_buffer_size这几样拧在一起——关掉hash_join可能让SQL快了,但INNODB_TABLESTATS还是旧的,下一条JOIN就又翻车。所以动作顺序不能乱:先查EXPLAIN FORMAT=JSON定位阶段,再比@@optimizer_switch,最后才动ANALYZE TABLE或Hint。











