5.7中DEPENDENT SUBQUERY导致子查询反复执行,8.0通过MATERIALIZE提示和LATERAL支持真正物化;升级后性能下降常因执行路径变化、隐式排序关闭或缓冲区未调优所致,需以EXPLAIN FORMAT=TREE验证物化是否生效。

DEPENDENT SUBQUERY 出现在 EXPLAIN 里,基本就说明子查询没被物化——这不是你 SQL 写得不好,而是 5.7 的优化器压根没能力把它当一次性结果来用。
5.7 中子查询为什么总被反复执行
只要子查询引用了外层字段(比如 WHERE t1.id = (SELECT ref_id FROM t2 WHERE t2.id = t1.id)),5.7 就大概率标记为 DEPENDENT SUBQUERY。它不会尝试缓存结果,而是外层每扫一行,内层就重跑一遍。
- 哪怕子查询本身有索引、只返回 1 行,优化器也看不到“可复用”,仍做嵌套循环
-
EXTRA列常带Using where; Using join buffer或Using temporary; Using filesort,是典型信号 - 主表 10 万行 × 子查询平均扫描 500 行 = 5000 万次 I/O,性能崩得悄无声息
8.0 怎么让子查询真正物化
8.0 不是“自动变快”,而是给了你可控的物化能力:默认启用 subquery_materialization_cost_based=ON,且支持 /*+ MATERIALIZE */ 提示。
-
/*+ MATERIALIZE */强制把子查询转成派生表,一次性计算、后续哈希连接 -
LATERAL允许在FROM子句中安全引用外层字段,并能被物化(如FROM orders o, LATERAL (SELECT COUNT(*) FROM logs l WHERE l.order_id = o.id) c) - 但
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对物化临时表影响更大;若没同步调大,小缓冲区会频繁触发磁盘临时表,比 5.7 更卡
验证物化是否真生效的唯一方式
别信“加了提示就一定起作用”,真正要看 EXPLAIN FORMAT=TREE 输出里有没有 materialize 节点,以及物化后的 rows 估算是否合理——否则优化器可能绕开你的提示,继续走老路。
函数索引、降序索引、隐藏索引这些新特性都容易吸引眼球,但子查询物化才是 8.0 真正改变执行模型的关键切口;它不显眼,但一旦失效,代价是 I/O 爆炸级的。


















