mysql 5.7子查询执行计划差主因是优化器无法物化而反复执行依赖子查询,导致嵌套循环和高i/o;8.0通过materialize提示、lateral支持及基于代价的物化决策改进,但需结合explain format=tree验证materialize节点与rows估算是否合理。

MySQL 5.7 中子查询执行计划差,不是写法错了,而是优化器根本没按你设想的方式执行——它常把 IN 或 = 后的子查询当成“依赖子查询”反复执行,外层每扫一行,内层就重跑一遍。
为什么 EXPLAIN 里总出现 DEPENDENT SUBQUERY?
这是 5.7 的典型信号:优化器无法物化子查询,只能走嵌套循环。哪怕子查询本身有索引,只要它引用了外层字段(比如 WHERE t1.id = t2.ref_id),就大概率被标记为 DEPENDENT SUBQUERY。
- 执行代价爆炸:主表 10 万行 × 子查询平均扫描 500 行 = 5000 万次 I/O
-
EXTRA列常带Using where; Using join buffer或Using temporary; Using filesort - 即使子查询只返回几行,优化器也看不到“结果集可复用”,仍重复计算
MySQL 8.0 真正改进了什么?
8.0 并不是“自动变快”,而是提供了更可控的物化能力与提示机制。关键变化是:MATERIALIZE 提示生效、LATERAL 支持、以及默认启用基于代价的物化决策(subquery_materialization_cost_based=ON)。
- 加
/*+ MATERIALIZE */后,(SELECT ...)会被当作一次性计算的派生表,后续走哈希连接而非嵌套循环 -
LATERAL允许在FROM中安全引用外层字段,且能被物化(如FROM orders o, LATERAL (SELECT COUNT(*) FROM logs l WHERE l.order_id = o.id) c) - 但注意:WHERE 或 SELECT 列表里的标量子查询(如
SELECT (SELECT name FROM users WHERE id = o.user_id))仍不会自动物化,必须手动改写
别信“升级就变快”,先看这三处是否匹配
很多团队升级到 8.0 后反而更慢,问题往往出在执行路径变化没被察觉:
- 5.7 用
IN可能碰巧走了松散索引扫描,8.0 改用物化后反而要建临时表——此时得补STRAIGHT_JOIN或强制索引USE INDEX - 8.0 默认关闭隐式排序,
GROUP BY后不再自动按分组字段排序,如果应用依赖这个行为,ORDER BY缺失会导致前端逻辑错乱,误判为“性能下降” -
sort_buffer_size和join_buffer_size在 8.0 中对物化临时表影响更大;若没同步调大,小缓冲区会频繁触发磁盘临时表,比 5.7 更卡
真正要盯住的,不是版本号,而是 EXPLAIN FORMAT=TREE 输出里有没有 materialize 节点,以及物化后的 rows 估算是否合理——否则提示再好,优化器也可能绕开它。











