dependent subquery 性能极差,因每行外层数据均触发一次子查询执行;其物化仅适用于非相关、无聚合、无排序、无 limit 的子查询,否则退化为嵌套循环;in/exists 语义差异导致无法自动优化,改用派生表或 join 更可控。

DEPENDENT SUBQUERY 出现一次,性能就可能掉一个数量级——这不是夸张,而是 MySQL 执行机制决定的硬伤。
为什么每行都触发一次子查询?
只要子查询里引用了外层表的列(比如 t1.id = t2.user_id),它就是「相关子查询」。MySQL 没法提前算出结果,只能对外层每一行都重跑一遍子查询。
- users 表 10 万行 → orders 子查询执行 10 万次
- 哪怕 orders 表有
user_id索引,每次仍要走一次索引查找 + 聚簇回表 - 如果子查询含
GROUP BY、ORDER BY RAND()或窗口函数,连物化机会都没有,必然退化为嵌套循环
物化(materialization)根本没生效?
MySQL 8.0+ 默认倾向物化,但只对「非相关 + 无聚合 + 无排序 + 无 LIMIT」的子查询生效。一旦不满足任一条件,优化器就放弃物化,转而用 DEPENDENT SUBQUERY。
-
EXPLAIN FORMAT=TREE里看到dependent subquery节点,且Extra含Using temporary; Using filesort,说明物化失败 - 物化临时表默认不建索引:内存小就用哈希,落盘就用 B+ 树,查起来比直接走索引还慢
- 子查询结果才几十行,外层却有百万行?此时物化开销(建表、拷数据、管理内存)反而拖累整体性能
IN 和 EXISTS 并不能自动提速
很多人以为把 IN 换成 EXISTS 就能触发物化或半连接——其实不会。两者语义不同,优化器只在严格等价时才重写。
-
NULL IN (1,2,NULL)返回NULL,EXISTS只返回布尔值,语义不等价 → 不重写 - 子查询里有
t1.id = t2.parent_id?无论写IN还是EXISTS,都是DEPENDENT SUBQUERY - 真正起作用的是「是否相关」,不是关键字本身;想绕开,得提前抽成派生表(
FROM (SELECT ...) AS tmp),且确保不引用外层列
JOIN 为什么更稳?
JOIN 的执行路径更透明:优化器能明确选择驱动表、复用索引、走 hash join 或 sort-merge,而子查询的物化与否全靠隐式判断,稍有偏差就崩。
- LEFT JOIN + IS NULL 替代
NOT IN,避免临时表和 NULL 陷阱 - INNER JOIN 替代
IN,让优化器有机会用semijoin,比物化快得多 - 派生表 + JOIN 比纯子查询更容易控制执行顺序和索引使用
EXPLAIN FORMAT=TREE 或 optimizer_trace 看清优化器到底选了哪条路。











