EXPLAIN中出现MATERIALIZED即表明半连接已启动;MySQL 5.7默认开启该优化,满足IN/EXISTS且无聚合、分组等限制时,优化器会将子查询物化为临时表并上拉执行,配合auto_key加速匹配。

EXPLAIN里看到MATERIALIZED就说明半连接启动了
MySQL 5.7默认开启半连接优化,只要子查询满足IN或EXISTS且不带聚合/分组,优化器就会尝试将其转为半连接。最直接的证据是EXPLAIN输出中出现select_type = MATERIALIZED或select_type = SEMI JOIN(后者较少见,多数版本显示为MATERIALIZED或DERIVED)。此时第二层查询会作为物化临时表先执行,再与外层表做等值匹配。
常见误判点:type = ALL + Extra = Using where并不等于没优化——关键要看子查询是否被上拉、是否生成临时表、是否复用索引。必须配合SHOW WARNINGS看重写后的SQL才能确认是否真走了半连接路径。
STRAIGHT_JOIN会禁用半连接优化
如果你在主查询里显式写了STRAIGHT_JOIN,MySQL会跳过半连接转换逻辑,强制按FROM子句顺序连接表。这不是bug,而是设计行为:半连接依赖优化器重排子查询执行时机,而STRAIGHT_JOIN本质是“禁止重排”的指令。
- 想验证是否被禁用?执行
EXPLAIN后对比select_type字段:原本该是MATERIALIZED的子查询,现在变成DEPENDENT SUBQUERY或UNION RESULT - 性能影响可能极大:比如
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE ...)加了STRAIGHT_JOIN后,t2可能被反复扫描,而非一次物化 - 替代方案:若需控制连接顺序,优先考虑添加
FORCE INDEX或调整统计信息,而不是直接上STRAIGHT_JOIN
物化临时表没索引?auto_key才是关键
半连接物化阶段生成的临时表默认没有主键或显式索引,但MySQL 5.7会在物化时自动创建auto_key(隐式哈希索引),用于加速外层表对临时结果的探查。这个索引不会出现在SHOW CREATE TABLE里,但可通过Handler_read_key状态变量观察到其使用频率。
容易踩的坑:
- 物化表字段类型若与外层JOIN列不一致(如
VARCHARvsCHAR、字符集不同),auto_key可能失效,退化为全表扫描 - 物化结果过大(超过
tmp_table_size或max_heap_table_size)会落地磁盘,此时auto_key降级为B+树索引,性能断崖下跌 -
NOT NULL约束缺失会导致优化器不敢用auto_key做NULL-aware短路,哪怕WHERE col IS NULL也扫完整个物化表
为什么OR子句反而让半连接变快?
看似反直觉,但真实案例中,带OR的IN子查询常比单个IN快数十倍,根本原因是MySQL对含OR的子查询选择放弃半连接,改用更激进的物化+索引策略。它会为每个子查询分支单独物化,并为每个物化表建auto_key,而单个子查询物化后可能因数据倾斜导致auto_key效率低下。
实操建议:
- 别盲目拆
IN为多个OR——仅当EXPLAIN显示单个物化耗时远高于预期时才试 - 用
SET optimizer_switch='semijoin=off'临时关闭半连接,对比执行时间,确认是否真由它引发慢查 - 真正稳定的解法是把子查询提前物化成普通表,加上合适索引,再用
JOIN代替IN
半连接不是银弹,它的触发条件、物化阈值、索引生成逻辑都藏在优化器决策深处。盯住EXPLAIN和SHOW WARNINGS,比背原理更能定位问题。


















