驱动表选错会导致全表扫描,MySQL优化器虽自动选择但常因统计信息过期或大小误判而失效;大表作驱动表时NLJ退化为暴力循环,EXPLAIN显示type=ALL且第一行为大表即为佐证,应强制STRAIGHT_JOIN、ANALYZE TABLE更新统计、避免ON中函数导致索引失效,并确保被驱动表关联字段有符合最左前缀的索引。

驱动表选错会导致全表扫描
MySQL优化器对 INNER JOIN 会自动选驱动表,但实际结果常不如预期——尤其当统计信息过期或表大小判断偏差时。一旦大表被选为驱动表,Nested Loop Join 就变成「大表每行都扫一遍小表」,rows 值爆炸式增长。
用 EXPLAIN 看执行计划,第一行的 table 就是驱动表。如果发现大表排在第一行,且 type 是 ALL 或 index,基本可以判定它正在暴力循环。
- 强制指定驱动顺序:改用
STRAIGHT_JOIN,把小表写在前面 - 更新统计信息:
ANALYZE TABLE让优化器重新估算行数 - 避免在
JOIN条件里混用函数,比如ON UPPER(a.name) = UPPER(b.name)会让索引失效,逼出Block Nested Loop
被驱动表连接字段没索引就触发BNL
当被驱动表的 ON 字段没有索引,MySQL无法快速定位匹配行,只能退化成 Block Nested Loop Join —— 把驱动表一批数据塞进 join_buffer,再逐块去被驱动表里比对。这时 Extra 列会出现 Using join buffer (Block Nested Loop),意味着内存和CPU都在硬扛。
注意:join_buffer_size 默认只有 256KB,远不够撑住中等规模的驱动表数据块。盲目调大可能挤占其他查询内存,反而引发抖动。
- 必须为被驱动表的
ON字段建索引,哪怕只是KEY (col) - 复合索引要遵循最左前缀,比如
ON a.x = b.x AND a.y = b.y,被驱动表索引得是(x, y),不能只建(y, x) -
LEFT JOIN的右表、RIGHT JOIN的左表,同样适用这条规则——它们就是被驱动表
哈希连接不是万能开关
Hash Join 从 MySQL 8.0.18 起支持,对大表等值 JOIN 是重大利好,但它不会自动启用,更不是“开了就快”。
触发条件很具体:必须是等值连接(=)、驱动表足够小(通常建议 hash_join_buffer_size 要够用),且优化器认为它比 Index Nested-Loop 更优。如果任一条件不满足,还是会回落到老算法。
- 检查是否启用:
SELECT @@optimizer_switch;确认包含hash_join=on - 强制走哈希连接:
/*+ HASH_JOIN(t1, t2) */提示(需 MySQL 8.0.24+) - 别指望它加速
LIKE '%xxx'或> 100这类非等值条件——哈希表只认精确匹配
ON 和 WHERE 的过滤时机差一个阶段
ON 在连接过程中生效,WHERE 是连接完成后再过滤。这个时间差直接影响驱动表的数据量和被驱动表的扫描范围。
比如 LEFT JOIN 中把右表的过滤条件写在 WHERE,会导致原本该保留的 NULL 行被干掉,逻辑错误;而写在 ON 里,才是真正的“左表全保留 + 右表按条件匹配”。
-
INNER JOIN下ON和WHERE对结果无影响,但ON能让优化器更早剪枝,减少中间结果集 - 多表
JOIN时,优先把高选择性条件(如status = 'done')放在对应表的ON子句里,而不是最后统一WHERE - 避免在
ON里对驱动表字段加条件,比如ON d.id = e.dept_id AND d.deleted = 0—— 这个deleted条件其实无效,因为驱动表早已读完
join_buffer_size 或升级 MySQL 版本都只是隔靴搔痒。


















