Hash Join
适用场景
Hash Join只适用于等值连接,对于>
, <
, <=
, >=
这样的查询连接还是需要nested loop
这种通用的连接算法来处理。如果连接key本来就是有序的或者需要排序,那么可能用Merge Join的代价会比Hash Join更小,此时Merge Join会更有优势。
Hash Join的代价估算:
COST = BUILD_COST + M_SCAN_COST + JOIN_CONDITION_COST + FILTER_COST
Hash Join的代价主要在于建立hash表、扫描M表、join条件连接和filter过滤,对于S表和M表都是只需要扫描一次即可,filter过滤是指t1.c2>t2.c2
这样的条件过滤,对于t1.c1>1
这样只涉及单表的条件会被下压,在做连接之前就被过滤了。
优化器处理过后,会生成一颗执行计划树,真正的实现过程根据执行计划的流程操作数据,由低向上地递归处理并返回数据。
Hash Join实现
Hash Join的实现分为build table
也就是被用来建立hash map的小表和probe table
,首先依次读取小表的数据,对于每一行数据根据连接条件生成一个hash map中的一个tuple
,数据缓存在内存中,如果内存放不下需要dump到外存。依次扫描探测表拿到每一行数据根据join condition
生成hash key映射hash map中对应的tuple
,tuple
对应的行和探测表的这一行有着同样的hash key, 这时并不能确定这两行就是满足条件的数据,需要再次过一遍join condition
和filter
,满足条件的数据集返回需要的投影列。
Hash Join本身的实现不判断哪个是小表,优化器生成执行计划时就已经确定了表的连接顺序,以左表为小表建立hash table,那对应的代价模型就会以左表作为小表来得出代价,这样根据代价生成的路径就是符合实现要求的。
LightDB 实测
tmp_idx_lt01,tmp_idx_lt02,tmp_idx_lt03三张表测试。
内连接
t1 10000000条, t2 10000000条
explain select * from tmp_idx_lt01 t1 join tmp_idx_lt02 t2 on t1.a=t2.a;
外连接
t1 10000000条, t3 100条
explain select * from tmp_idx_lt01 t1 full join tmp_idx_lt03 t3 on t1.a=t3.a;
explain select * from tmp_idx_lt01 t1 left join tmp_idx_lt03 t3 on t1.a=t3.a;
explain select * from tmp_idx_lt01 t1 right join tmp_idx_lt03 t3 on t1.a=t3.a;
可以看出,left join
为了能够以小表建立hash table被转换为了right join
。