MySQL 8.0.18+ 默认对无索引等值连接使用 Hash Join,但需满足类型兼容、优化器估算更优等条件;有索引时仍首选 NLJ。

MySQL 8.0.18+ 默认用哪种 JOIN 算法?
MySQL 8.0.18 起,Hash Join 成为非索引连接场景的默认算法,但前提是:被驱动表(右表)没有可用索引、且连接字段类型兼容、连接条件是等值(=),并且优化器估算其比 Block Nested-Loop Join(BNL)更优。
注意:Nested-Loop Join(NLJ)仍是索引存在时的首选——只要驱动表的每行能通过索引快速定位被驱动表匹配行,优化器几乎总选 NLJ,因为它 IO 少、延迟低。
-
Hash Join只在EXPLAIN的Extra列中显示为Using join buffer (hash join) - 若看到
Using where; Using join buffer (Block Nested Loop),说明走的是 BNL(一种带缓冲的 NLJ 变种) - 纯
Using join buffer(无括号说明)通常表示 BNL;而Using index condition或Using index出现时,基本可判定是 NLJ
为什么有时候强制用 Hash Join 反而变慢?
Hash Join 需把被驱动表(或其连接字段 + 主键)整个加载进内存构建哈希表。一旦该表太大(比如 > 数百 MB)、或内存不足(join_buffer_size 不够),就会退化成多次磁盘分片处理,性能断崖式下跌。
典型踩坑场景:
- 被驱动表含
TEXT/BLOB字段,即使只 select 主键,也可能因 MySQL 内部物化逻辑导致哈希表膨胀 -
join_buffer_size设置过小(默认仅 256KB),而实际需要数 MB —— 此时应调大,但注意它是 per-connection 的,别盲目设到 1GB+ - 连接字段有隐式类型转换(如
INTvsVARCHAR),哈希计算前需统一类型,开销增大且可能失准 - 非等值连接(
,BETWEEN)完全不支持Hash Join,优化器会直接忽略
如何干预 JOIN 算法选择?
MySQL 不提供 USE HASH JOIN 这类 HINT,但可通过以下方式间接引导:
- 删掉被驱动表上“干扰性”索引:有时一个低效的索引会让优化器误判 NLJ 成本更低,删除后反而触发
Hash Join - 用
STRAIGHT_JOIN固定驱动表顺序,再配合FORCE INDEX或IGNORE INDEX控制是否走索引,从而影响算法路径 - 调大
join_buffer_size(会话级即可):例如SET SESSION join_buffer_size = 4194304;(4MB),对中等规模被驱动表提升明显 - 检查
EXPLAIN FORMAT=TREE输出,它会明确写出-> Hash join或-> Nested loop join,比传统EXPLAIN更直观
BNL 和 Simple Nested Loop 的区别在哪?
老版本 MySQL 常见的 Block Nested-Loop Join(BNL)本质是把驱动表数据分块读入 join_buffer,再批量去被驱动表做匹配,减少被驱动表扫描次数;而 Simple Nested Loop(未启用 BNL 时)是驱动表每行都单独扫一遍被驱动表,IO 放大严重。
关键控制开关是 optimizer_switch 中的 block_nested_loop 标志(默认 ON)。禁用它:
SET optimizer_switch='block_nested_loop=off';会强制退回到 Simple Nested Loop —— 仅用于调试对比,生产环境绝不建议。
真正要关注的是:当 join_buffer_size 太小,BNL 的“块”太小,反而导致更多次被驱动表扫描;此时不如让驱动表走索引,切回 NLJ。
哈希表构建和 NLJ 的索引查找,本质上是内存换 IO 和 IO 换 CPU 的权衡,没有银弹——得看你的数据分布、内存余量、以及最痛的是延迟还是吞吐。










