③ 对结果集的join键做hash运算,将数据分散到相应partition的bulket中
当运算完成后,如果键值唯一性较高的话,bulket里的数据会比较均匀,也有可能有的桶里面数据会是空的
这样bitmap上对应的标志位就是0,有数据的桶,标志位会是1
④ 开始扫描第二张表,对jion键做hash运算,确定应该到某个partition的某个bulket去探测
探测之前,会看这个bulket的bitmap是否会1,如果为0,表示没数据,这行就直接丢弃掉
⑤ 如果bitmap为1,则在桶内做精确匹配,判断OK后,返回数据
这个是最优的hash join,他的成本基本是两张表的full table scan,在加微量的hash运算
博客开篇的那幅图描述的也就是这种情况
⑵ one-pass
如果进程的pga很小,或者驱动表结果集很大,超过了hash area的大小,会怎么办?
当然会用到临时表空间,此时oracle的处理方式稍微复杂点需奥注意上面提到的有个partition的概念
可以这么理解,数据是经过两次hash运算的,先确定你的partition,再确定你的bulket
假设hash area小于整个hash table,但至少大于一个partition的size,这个时候走的就是one-pass
当我们生成好hash表后,状况是部分partition留在内存中,其他的partition留在磁盘临时表空间中
当然也有可能某个partition一半在内存,一半在磁盘,剩下的步骤大致如下:
① 扫描第二张表,对join键做hash运算,确定好对应的partition和bulket
② 查看bitmap,确定bulket是否有数据,没有则直接丢弃
③ 如果有数据,并且这个partition是在内存中的,就进入对应的桶去精确匹配,能匹配上,就返回这行数据,否则丢弃
④ 如果partition是在磁盘上的,则将这行数据放入磁盘中暂存起来,保存的形式也是partition,bulket的方式
⑤ 当第二张表被扫描完后,剩下的是驱动表和探测表生成的一大堆partition,保留在磁盘上
⑥ 由于两边的数据都按照相同的hash算法做了partition和bulket,现在只要成对的比较两边partition数据即可
并且在比较的时候,oracle也做了优化处理,没有严格的驱动与被驱动关系
他会在partition对中选较小的一个作为驱动来进行,直到磁盘上所有的partition对都join完
可以发现,相比optimal,他多出的成本是对于无法放入内存的partition,重新读取了一次,所以称为one-pass
⑶ multi-pass
这是最复杂,最糟糕的hash join
此时hash area小到连一个partition也容纳不下,当扫描好驱动表后
可能只有半个partition留在hash area中,另半个加其他的partition全在磁盘上
剩下的步骤和one-pass比价类似,不同的是针对partition的处理
由于驱动表只有半个partition在内存中,探测表对应的partition数据做探测时
如果匹配不上,这行还不能直接丢弃,需要继续保留到磁盘,和驱动表剩下的半个partition再做join
这里举例的是内存可以装下半个partition,如果装的更少的话,反复join的次数将更多
当发生multi-pass时,partition物理读的次数会显著增加
㈦ Hash Join的位图
这个位图包含了每个hash分区是否有有值的信息。它记录了有数据的分区的hash值
这个位图的最大作用就是,如果probe input中的数据没有与内存中的hash表匹配上
先查看这个位图,以决定是否将没有匹配的数据写入磁盘
那些不可能匹配到的数据(即位图上对应的分区没有数据)就不再写入磁盘
㈧ 小结
① 确认小表是驱动表
② 确认涉及到的表和连接键分析过了
③ 如果在连接键上数据不均匀的话,建议做柱状图
④ 如果可以,调大hash_area_size的大小或pga_aggregate_target的值
⑤ Hash Join适合于小表与大表连接、返回大型结果集的连接