首页/数据/数据库速查/Hash Join(8.0.18+)与联合缓冲区
性能与调优57 条

Hash Join(8.0.18+)与联合缓冲区

无索引连表时 8.0 用 Hash Join 替代 BNL:为小表建哈希表、扫描大表探测,内存不足时落盘分批;EXPLAIN 显示 Using join buffer (hash join)。

数据库速查数据更新于 2026-10-13

Hash Join 是什么

8.0.18 起,MySQL 在没有可用索引的等值连接场景下会用 Hash Join 替代旧的 Block Nested Loop:先扫描较小的表构建哈希表,再扫描另一张表逐行探测。内存不足时哈希表会分批落磁盘,性能随之明显下降。执行计划里会显示 Using join buffer (hash join)。它解决的是「缺索引时连接不至于完全没法跑」的问题,而不是优化目标——被驱动表有合适索引时,嵌套循环通常仍然更快。

基本信息

版本要求MySQL 8.0.18 起默认启用
适用条件存在等值连接条件,且无法有效使用索引
计划特征EXPLAIN 的 Extra 出现 Using join buffer (hash join)
内存参数join_buffer_size 影响每个连接可用的连接缓冲大小
退化表现内存不足时哈希表落磁盘,执行时间显著拉长

典型使用场景

  • 临时分析查询:跑临时统计时表上没有合适索引,Hash Join 保证查询可完成
  • 索引缺失的过渡期:索引正在创建或暂不可加时,先让查询可跑
  • 连接字段类型不一致:两侧列类型不同导致索引用不上,先验证 Hash Join 效果
  • 报表查询优化:确认是 Hash Join 后评估是否补索引或改写查询
  • 计划对比:升级到 8.0 后对比同一语句在 5.7 与 8.0 上的计划差异

观察与调参

观察与调参
-- 查看连接方式,重点看 Extra 一列
EXPLAIN SELECT a.id, b.code
FROM appdb.a JOIN appdb.b ON b.code = a.code;
-- Extra 出现 Using join buffer (hash join) 说明走了哈希连接
-- 连接缓冲是每个连接(更准确说是每个需要缓冲的连接)单独分配
SHOW VARIABLES LIKE 'join_buffer_size';
SET SESSION join_buffer_size = 4194304;   -- 单会话临时调整,4MB
-- 对比调整后的计划与耗时,再决定是否补索引
EXPLAIN ANALYZE SELECT a.id, b.code FROM appdb.a JOIN appdb.b ON b.code = a.code;

连接方式对照

方式计划特征适用条件
嵌套循环无 join buffer 提示被驱动表连接列有索引
Hash JoinUsing join buffer (hash join)等值连接且无可用索引
块嵌套循环Using join buffer (Block Nested Loop)8.0.18 之前的默认退化方式
落盘哈希耗时骤增哈希表超出可用连接缓冲空间
看到 Hash Join 的正确反应是「这里缺索引或索引没被用上」,而不是调大 join_buffer_size。缓冲是每连接分配的,全局调大在高并发下会迅速吃光内存。
连接条件两侧的类型、字符集与排序规则不一致时索引会失效,这也是 Hash Join 出现的常见原因,核对表定义比调参数更有效。

易错点

  • 把 join_buffer_size 调到 GB 级:每个连接独立分配,高并发下直接触发内存不足
  • 看到 Hash Join 就认为已优化:几十万行以上的哈希构建会持续消耗 CPU,正确动作是补索引或改写查询
  • 忽略连接列类型与字符集差异:索引失效的根因没解决,换个写法仍然不会走索引
  • 在生产上直接跑 EXPLAIN ANALYZE 大查询:它会实际执行语句,大表上开销可观,应在测试环境或低峰执行
  • 以为所有连接都会用 Hash Join:它只适用于等值连接,非等值连接仍需其他方式处理

数据来源与参考

  • MySQL 8.0 Reference Manual: Hash Join Optimization

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌