首批通过分布式安全可靠测评,为关键业务系统打造
Hash Join
更新时间:2026-04-10 12:01:00
什么是 Hash Join
Hash Join 的原理是使用左表的数据构建哈希表,右表通过探测哈希表的方式完成联接运算。其执行过程的伪代码如下所示。
step 1 build hash table:
for row_1 in (select * from left_table)
loop
hash_value = HASH(row_1)
insert_hash_table(hash_value, row_1)
end loop
step 2 probe hash table:
for row_2 in (select * from right_table)
loop
hash_value = HASH(row_2)
row = lookup_hash_table(hash_value, row_2)
if match join condition(row, row_2)
then
output (row, row_2)
end if
end loop
Hash Join 计划如下所示。
Query Plan:
======================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
--------------------------------------
|0 |HASH JOIN | |99000 |194200|
|1 | TABLE SCAN|t1 |100000 |41911 |
|2 | TABLE SCAN|t2 |100000 |41911 |
======================================
Outputs & filters:
-------------------------------------
0 - output([1]), filter(nil),
equal_conds([t1.c1 = t2.c1]), other_conds(nil)
1 - output([t1.c1]), filter(nil),
access([t1.c1]), partitions(p0)
2 - output([t2.c1]), filter(nil),
access([t2.c1]), partitions(p0)
优化器什么时候选择 Hash Join
一般情况下,当需要 join 的数据量比较大时(或者需要 join 的小表的比例很大),优化器会考虑 Hash Join,而且需要是等值联接。
较小的数据集使用比较少的内存物化数据,Hash Join 性能最好。在这种情况下,联接成本只是对两个数据集进行一次读取。因为哈希表在内存中,所以数据库可以不需要通过锁访问数据行。此技术通过避免重复锁存和读取数据库缓冲区缓存中的块的必要性来减少逻辑 I/O。
如果数据集较大,内存不足以存放,则数据库对数据源进行分区,并逐个分区进行联接。这会使用大量排序区域内存和临时表空间的 I/O。这种方法仍然有高效场景,尤其是当数据库使用并行查询时。
如何控制优化器使用 Hash Join
最直接的控制手段就是通过 hint 指定联接算法,可以通过使用 USE_HASH 指示优化器使用 Hash Join 算法。通常还需要配合使用 LEADING Hint,因为指定 Hash Join 算法之后计划的成本可能不是最低的,会被其他成本更低的计划(联接次序不同)覆盖。USE_HASH 的参数为联接的右表。
使用示例。在默认情况下,优化器自主选择了 Nested Loops Joins 算法。
explain select 1 from t1, t2 where t1.c1 = t2.c1;
Query Plan:
============================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
--------------------------------------------
|0 |NESTED-LOOP JOIN| |99000 |4120845|
|1 | TABLE SCAN |t1 |100000 |41911 |
|2 | TABLE GET |t2 |1 |40 |
============================================
Outputs & filters:
-------------------------------------
0 - output([1]), filter(nil),
conds(nil), nl_params_([t1.c1])
1 - output([t1.c1]), filter(nil),
access([t1.c1]), partitions(p0)
2 - output([1]), filter(nil),
access([t2.c1]), partitions(p0)
为了控制优化器使用 Hash Join 算法,我们可以使用 hint 控制。
explain select /*+leading(t1 t2) use_hash(t2)*/ 1 from t1, t2 where t1.c1 = t2.c1;
Query Plan:
======================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
--------------------------------------
|0 |HASH JOIN | |99000 |194200|
|1 | TABLE SCAN|t1 |100000 |41911 |
|2 | TABLE SCAN|t2 |100000 |41911 |
======================================
Outputs & filters:
-------------------------------------
0 - output([1]), filter(nil),
equal_conds([t1.c1 = t2.c1]), other_conds(nil)
1 - output([t1.c1]), filter(nil),
access([t1.c1]), partitions(p0)
2 - output([t2.c1]), filter(nil),
access([t2.c1]), partitions(p0)
为了更好的使用 Hash Join 算法,在控制优化器时需要注意以下两点:
- 尽量使用数据量比较小的数据源构建哈希表,也就是作为驱动表。
- 不能对非等值联接使用 Hash Join 算法。