OPTIMAL MODE:
Optimal方式的hash join相对来说非常简单,在这个例子(optimal.doc)里面,一共需要2个partition,这也是hash join中最小的partition分配数,随着work area的增大,partition的数目也会增多,partition的分配规律遵守2的n次方这样的规则分配。在经过build table的scan以后,hash table被建立,每条记录被hash后对应的hash bucket也被填充,与此相对应的bitmap vector也bit位也被设置,这个时候开始probe table的scan,首先通过同样的hash function对join key进行运算,然后对比bitmap vector中的bit是否被标志,如果没被标志那么表示这条记录不符合关联的要求直接被丢弃。如果bit位被设置,那么表示这个值可能与hash bucket中保存的值一致,因为hash算法会有碰撞,所以一个bucket中会有不同的值存在,所以将会对比这个值和hash bucket中保存的值是不是相同,如果相同那么输出给客户端,等probe table扫描完毕,所有结果都输出给客户端。
optimal.doc 点击下载trace 文件 点击下载trace 文件
ONEPASS MODE:
当我们修改了hash_area_size到2m后,hash join的运行模式变成了onepass状态,从上面的trace(onepass.doc)文件中可以发现,partition变成了4个
### Partition Distribution ###
Partition:0 rows:49933 clusters:5 slots:1 kept=0
Partition:1 rows:49674 clusters:5 slots:1 kept=0 Partition:2 rows:49943 clusters:5 slots:4 kept=0
Partition:3 rows:50451 clusters:5 slots:5 kept=1
其中partition 3可以被放在work area中,其他几个partition不可避免的要写出到磁盘上。接下来开始probe table的scan,在扫描的过程中经过bitmap vector的过滤,不符合的记录被丢弃,因为bitmapVector中也保存了每个hash bucket是否在内存还是在磁盘,所以符合的记录就会去查到底这个hash bucket位于哪里,如果在内存,直接比较确切值,丢弃或者输出给客户端(从trace文件中看不出这个过程,trace文件看起来是dump所有不能放入内存的partitition,然后读入partition pair做hash join)。如果在磁盘,那么probe table的这条记录也写出到磁盘,形成一个partition pair。在partition 3的数据join完毕,接下来是partition 2的所有部分被读入内存,同时probe table相对的partition也被读入内存进行hash join,然后是partition 1,partition 0。可以看到这里都只读了一次probe table partition,所以我们称之为onepass hash join.
onepass.doc 点击下载trace 文件
MULTIPASS MODE:
这个就是multipass hash join的过程,trace文件异常的长,首先分配了2个partitions,没有一个partition能被完全放在work area里面
### Partition Distribution ###
Partition:0 rows:99876 clusters:10 slots:1 kept=0
Partition:1 rows:100125 clusters:10 slots:4 kept=0
首先是build table的scan, Work area里面容纳了partition 1的4个slot和partition 0的一个slot,另外还有一个slot是为了i/o用的。每当一个slot(可能是一个block)满后将会被写出到磁盘,等build table scan完毕,磁盘上保存了2个partition的内容,并且bitmap vector也被建立了。这时开始了probe table的scan,系统为probe table的每个partition分配了一个slot,还分配一个slot作为读入probe table的缓冲,通过bitmap vector的过滤,符合条件的记录被填充到probe partition的slot中并与build table partition的slot来比较确切值。这个步骤完了以后work area里面应该存在一个build table partition 0的slot,一个build table partition 1的slot,一个写出缓冲的slot,一个读入缓冲的slot,一个probe table partition 0的slot,一个probe table partition 1的slot,一共6个slot,刚好填满所有可分配的slot。这个过程中间可能会有在内存中匹配的行返回,然后开始从磁盘读取partition pair开始join,经过10次probe partition的读取。partition 0也是经过了10次probe partition的读取,这就带来了很多i/o,导致hash join的性能急剧下降。Oracle在进行multipass的过程中会有2次hash function的存在。Oracle将会读入磁盘上的build table partition再进行一次hash生成一些subpartition,这样的话每次读入probe table subpartition即可,而不用多次读入probe partition导致过大的i/o.不过在这个trace文件中并没有发现这个过程。的个和的一个另外还有一个是为了用的。每当一个(可能是一个)满后将会被写出到磁盘,等完毕,磁盘上保存了个的内容并且也被建立了。这时开始了的,系统为的每个分配了一个,还分配一个作为读入的缓冲,通过的过滤,符合条件的记录被填充到的中并与的来比较确切值。这个步骤完了以后里面应该存在一个的,一个的,一个写出缓冲的一个读入缓冲的,一个的,一个的,一共个,刚好填满所有可分配的。这个过程中间可能会有在内存中匹配的行返回,然后开始从磁盘读取开始,经过次的读取。也是经过了次的读取,这就带来了很多,导致的性能急剧下降。在进行的过程中会有次的存在。将会读入磁盘上的再进行一次生成一些,这样的话每次读入即可,而不用多次读入导致过大的不过在这个文件中并没有发现这个过程。里面容纳了的个和的一个另外还有一个是为了用的。每当一个(可能是一个)满后将会被写出到磁盘,等完毕,磁盘上保存了个的内容并且也被建立了。这时开始了的,系统为的每个分配了一个,还分配一个作为读入的缓冲,通过的过滤,符合条件的记录被填充到的中并与的来比较确切值。这个步骤完了以后里面应该存在一个的,一个的,一个写出缓冲的一个读入缓冲的,一个的,一个的,一共个,刚好填满所有可分配的。这个过程中间可能会有在内存中匹配的行返回,然后开始从磁盘读取开始,经过次的读取。也是经过了次的读取,这就带来了很多,导致的性能急剧下降。在进行的过程中会有次的存在。将会读入磁盘上的再进行一次生成一些,这样的话每次读入即可,而不用多次读入导致过大的不过在这个文件中并没有发现这个过程。另外还有一个trace文件反映了ROLE REVERSAL,oracle在进行join时会去评估build partition和probe partition的大小,如果发行probe partition小于build partition,那么会对换两者的角色,对原来的probe partition建立hash table,拿原来的build partition来匹配,这种方法也在一定程度上减少了i/o,提高了效率。
*** HASH JOIN GET FLUSHED PARTITIONS (PHASE 2) ***
Getting a pair of flushed partions.
BUILD PARTION: nrows:24948 size=(3 slots, 384K)
PROBE PARTION: nrows:12410 size=(2 slots, 256K)
ROLE REVERSAL OCCURRED
### Hash table overall statistics ###
Total buckets: 262144 Empty buckets: 250012 Non-empty buckets: 12132
Total number of rows: 12410
Maximum number of rows in a bucket: 3
Average number of rows in non-empty buckets: 1.022915
上面这些只是本人基于trace文件对hash join运行模式的推断,不一定能反正hash join真实的运行情况,所以欢迎大家来讨论! multipass.doc 点击下载trace 文件
