mysql 5.7子查询性能差主因是优化器未正确物化或误判为相关子查询;判断依据是explain中select_type为materialized/semispace即已物化,若为dependent subquery且extra反复执行,则属嵌套循环,性能急剧下降。

MySQL 5.7 中子查询性能差,大概率不是写法本身有问题,而是优化器没走对路——要么该物化(MATERIALIZE)却没物化,要么该转 JON 却硬扛着跑依赖子查询(DEPENDENT SUBQUERY)。
怎么判断子查询有没有被物化?
关键看 EXPLAIN 输出里的 select_type 和 Extra:
- 如果出现
MATERIALIZED或SEMISPACE,说明触发了物化优化(5.7+ 默认启用semijoin+materialization) - 如果仍是
DEPENDENT SUBQUERY,且外层每查一行它就执行一遍,那就是“循环嵌套”,性能必然崩——尤其外层结果集 >1000 行时,子查询可能被执行上千次 -
Extra列出现Using temporary但没Using filesort,通常是物化成功;若同时有Using where; Using join buffer,可能是优化器退化到了 Block Nested-Loop
为什么有时加了 /*+ NO_SEMIJOIN() */ 反而更快?
这不是 bug,是优化器在特定条件下误判了成本。比如:
- 子查询结果集小(
- 子查询含隐式类型转换(如
WHERE id IN (SELECT user_id FROM logs WHERE event_code = 'pay'),而event_code是VARCHAR但没索引),导致物化后无法高效匹配 - 统计信息过期(
ANALYZE TABLE没跑过),优化器低估了子查询行数,强行物化后内存溢出,降级为磁盘临时表
这时加提示强制关闭 semijoin,让优化器退回到传统嵌套循环或尝试用 IN 走 range 索引扫描,可能更稳。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
什么时候必须把 IN 子查询改成 JOIN?
不是所有都能改,但以下情况不改就是留坑:
- 子查询带外层引用,例如
WHERE o.id IN (SELECT order_id FROM refunds r WHERE r.order_id = o.id)→ 必须转EXISTS或LEFT JOIN ... IS NOT NULL,否则永远是DEPENDENT SUBQUERY - 子查询结果可能含
NULL,例如SELECT * FROM users WHERE id IN (SELECT user_id FROM events),而events.user_id允许为NULL→IN遇到NULL整体返回空,JOIN直接过滤掉,行为不等价,需补WHERE user_id IS NOT NULL - 子查询有
DISTINCT或GROUP BY→ 改JOIN后必须显式加DISTINCT或GROUP BY,否则因笛卡尔积导致结果膨胀
手动物化临时表的实操要点
当优化器始终不按预期物化,或子查询逻辑太复杂(含窗口函数、多层嵌套),直接人工控制最可靠:
- 用
CREATE TEMPORARY TABLE tmp AS SELECT DISTINCT user_id FROM orders WHERE status = 'shipped' AND create_time > '2026-03-01'提前固化结果 - 立刻建索引:
CREATE INDEX idx_user ON tmp(user_id)—— 不建索引的临时表跟全表扫描没区别 - 外层改写为
JOIN tmp ON t.user_id = tmp.user_id,别用IN (SELECT ... FROM tmp),后者仍可能触发依赖执行 - 高并发场景注意表名冲突:可用
CONCAT('tmp_', CONNECTION_ID())动态构造临时表名,避免会话间干扰
临时表方案看似“笨”,但它绕过了优化器的所有猜测,唯一容易被忽略的是:忘了在 tmp 上建索引,或者误以为 TEMPORARY 表自动带主键/唯一约束——它什么都没有,纯靠你手建。










