Navicat 17 执行计划中看不到 Hash Join,根本原因在于 MySQL 8.0+ 才支持且需满足条件:被驱动表无可用索引、优化器估算 NLJ 成本过高、optimizer_switch 中 hash_join=on 开启;可通过 NO_INDEX Hint 强制触发,并在执行计划的 Join Type 和 Extra 列验证。
navicat 17 执行计划里看不到 hash join?不是 navicat 的问题,是 mysql 优化器根本没选它——得从查询写法和 hint 入手干预。
MySQL 8.0+ 才默认启用 Hash Join
Navicat 17 只是展示执行计划的工具,真正决定是否用 Hash Join 的是 MySQL 服务端。5.7 及更早版本压根不支持,8.0 开始才作为连接算法之一引入,且默认只在特定条件下启用:
- 被驱动表的关联字段 没有可用索引(比如
ON t1.a = t2.b,而t2.b没索引) - 优化器估算
Nested Loop Join成本过高(比如大表对大表、无索引字段 join) -
optimizer_switch中的hash_join=on必须开启(默认是 on,但可能被改过)
验证当前开关状态:
SELECT @@optimizer_switch;检查输出中是否含
hash_join=on。若为 off,需执行:SET GLOBAL optimizer_switch='hash_join=on';(注意:需 SUPER 权限,且仅对新连接生效)
用 NO_INDEX Hint 强制“逼出” Hash Join
当两表关联字段明明有索引,但你想绕过 Nested Loop Join 测试 Hash Join 效果时,最直接的办法是让优化器“看不见”那个索引:
- 假设语句是
SELECT * FROM t1 JOIN t2 ON t1.id = t2.c1,而t2.c1有索引 - 加 Hint 禁用该索引:
SELECT /*+ NO_INDEX(t2) */ * FROM t1 JOIN t2 ON t1.id = t2.c1;
- 或更精准地禁用单列:
SELECT /*+ NO_INDEX(t2 (c1)) */ * FROM t1 JOIN t2 ON t1.id = t2.c1;
执行后在 Navicat 17 的执行计划中,Extra 列会显示 Using join buffer (hash join),操作类型(Type 或 Join Type)会标为 Hash Join。
为什么有时 Hint 不生效?常见卡点
Hint 是建议,不是强制指令。以下情况会导致 NO_INDEX 或其它 Hint 失效:
- Hint 写错位置:必须紧贴在
SELECT关键字后,且用/*+开头、*/结尾,不能写成/*或漏空格 - 表别名未同步:如果 SQL 里用了别名(如
FROM t2 AS a),Hint 中必须写t2还是a?答案是——a,即实际参与执行计划的名称 - QB_NAME 作用域混淆:子查询中 Hint 默认只作用于当前 Query Block;若要控制子查询里的表,得显式指定,例如
/*+ NO_INDEX(@sel_2 t2) */ - 统计信息严重失真:即使禁了索引,优化器仍可能因错误的行数估算,认为
Hash Join更贵,从而回退到Block Nested Loop
Navicat 17 查看 Hash Join 计划的关键观察点
别只盯着“有没有 Hash Join”四个字。在 Navicat 执行计划面板中,重点核对这几项:
-
Type或Join Type列:明确显示Hash Join,而非ALL或index -
Extra列:含Using join buffer (hash join),这是最可靠标识 -
Rows和Filtered:对比禁用索引前后的预估行数,若突增说明优化器确实放弃了索引扫描 -
Key列:应为NULL或空,表示未使用任何索引进行关联
真正难的不是触发 Hash Join,而是判断它是否真的比 Nested Loop 更优——这取决于数据分布、内存配置(join_buffer_size)、以及你是否愿意为 hash 构建阶段多消耗一点 CPU 和临时内存。


















