mysql 8.0 explain执行计划变差主因是优化器更严格准确,暴露原有sql或索引缺陷;需用format=tree定位过滤下推、物化等问题,并执行analyze table校准统计。

升级 MySQL 8.0 后 EXPLAIN 显示的执行计划变差,大概率不是“优化器退化”,而是旧版本某些行为被修正、统计信息或元数据结构变化暴露了原有 SQL 或索引设计的隐性缺陷。
为什么 EXPLAIN 的 type/rows/key 和 5.7 差这么多?
MySQL 8.0 优化器在成本模型、统计信息采集方式、子查询物化策略上做了实质性调整。常见触发点包括:
-
type从ref退化为ALL:很可能是隐式类型转换(如WHERE user_id = '123',而user_id是INT)在 8.0 中更严格判定为不可用索引;检查key是否为空、key_len是否异常小 -
rows预估暴涨:旧版依赖过时的ANALYZE TABLE统计,8.0 默认启用innodb_stats_persistent=ON,但若未手动更新,估算会严重失真;执行ANALYZE TABLE tbl_name;再看EXPLAIN -
Extra出现Using temporary; Using filesort:8.0 对ORDER BY + LIMIT的优化更保守,尤其当排序字段不在索引覆盖范围内时;确认是否缺失覆盖索引或能否改写为延迟关联
视图/子查询在 EXPLAIN 中变成 derived 或 materialized 怎么办?
8.0 默认开启 derived_merge=ON,本意是合并派生表提升性能,但对含聚合、DISTINCT 或无法下推条件的视图反而导致物化开销。典型表现是 EXPLAIN 中出现 MATERIALIZED 或 DERIVED,且 rows 值远超预期。
- 先验证是否真需要视图:监控类查询(如查
sys.innodb_lock_waits)直接展开底层performance_schema表更稳,避免元数据锁和可见性检查开销 - 强制禁用合并:在视图定义或外部查询中加优化器提示
/*+ NO_MERGE(t) */,其中t是派生表别名 - 替换子查询为
JOIN:比如SELECT * FROM t1 WHERE id IN (SELECT id FROM t2)改成SELECT t1.* FROM t1 JOIN t2 ON t1.id = t2.id,让 8.0 更容易做连接重排
直方图建了但 EXPLAIN rows 没变?
直方图不是“建了就生效”,它只在优化器对某列的选择率估算严重失准时才被读取,且有硬性前提:
- 该列必须出现在
WHERE/JOIN条件中,且未被函数包裹(UPPER(col)、YEAR(dt)会直接绕过直方图) - 该列无有效索引,或索引未命中前导列(如有索引
(a,b)但查询是WHERE b = ?,直方图才可能起作用) - 建完后必须验证:查
information_schema.COLUMN_STATISTICS确认HISTOGRAM字段返回非空 JSON;再用EXPLAIN FORMAT=JSON对比前后"rows"变化 - 选错类型会适得其反:低基数枚举列(如
status)要用SINGLETON,而非默认EQ_HEIGHT;建错会导致优化器更迷糊
FORMAT=TREE 比传统格式多看出什么关键信息?
EXPLAIN FORMAT=TREE 是 8.0+ 真正能定位“为什么没走索引”的利器,它把嵌套逻辑可视化:
- 看到
-> Filter: (t1.id > 100)这种行,说明该条件没下推到存储引擎层,而是在 Server 层回表后过滤——意味着索引没覆盖查询所需字段,或用了不支持下推的表达式 - 出现
-> Materialize with deduplication,表示子查询被物化去重,此时应检查是否可改写为EXISTS或添加合适索引避免物化 - 对比
FORMAT=TRADITIONAL和FORMAT=TREE的rows,若后者显著更小,说明优化器在新格式下利用了更细粒度的统计(比如直方图或条件间相关性),这时优先信 TREE 版本
真正难的不是看懂 EXPLAIN,而是区分哪些差异是“优化器更准了”(该接受),哪些是“SQL 或索引本身有硬伤”(该改)。别急着调配置,先用 FORMAT=TREE 和 ANALYZE TABLE 把预估误差收窄,再决定动 SQL 还是动索引。











