ORACLE连接实现之HASH JOIN

最近,查阅了部分关于HASH JOIN的资料,现整理总结如下,以备忘。

HASH JOIN是oracle在7.3版本中引入的一种表连接方式,以补充NESTED LOOP 和sort merge。HASH JOIN具有以下特征:


1.只可以运行在CBO模式下

2.由于采用了hash函数的计算方式,因此只适用于等值操作

3.对hash_area_size的大小非常敏感,过大或者过小都会影响到执行效率,因此。建议采用ORACLE的自动内存管理机制;

4.hash join属于CPU密集型操作(用于hash运算等),尤其在并行模式下,对cpu的效果更加明显,因此在cpu资源非常紧张的情况下,我们可以尝试屏蔽hashjoin,以便提高数据库的整体性能。

5.作为小表的数据在链接列上最好是分布均匀的


与hash join 相关的参数:

1.HASH_JOIN_ENABLED

在10g的版本中,已成为隐藏函数_hash_join_enabled,(要查看隐藏函数,参考:http://blog.csdn.net/yidian815/article/details/12154567)。我们可以在system和session级别来修改这个参数。

2.HASH_AREA_SIZE

这个参数控制hash join可用的内存区域的大小,默认情况下,该参数为SORT_AREA_SIZE的两倍,ORACLE不建议我们手动修改该参数的值,为了提高hashjoin的性能,最好保证整个小表集合的数据可以完全放入内存中,但是在完全放入内存后,再继续增加hash erea意义不大,而且可能使效率下降(例如,消耗在内存管理)。

在单个session中,可能同时存在多个hash area,因此一个sql查询可能同时存在多个hash join。

3.worderea_size_policy

关于该参数的解释,请看:http://blog.csdn.net/yidian815/article/details/12158537

4.pga_aggregate_target

该参数指定pga的内存管理是否使用自动内存管理,当采用自动内存管理时,单个session的占用内存不可以超过pga_aggregate_target的5%,而如果采用并行模式,则所有并行进程的总和不可以超过30%。为了使用大的内存用于hash join,可以将wordarea_size_police设置为手工模式,以便于手工指定各个area的内存大小。


5。HAHS_MULTIBLOCK_IO_COUNT

这个参数决定每次读入hash area的数据块数量。因此它会对IO性能产生影响。在8.0及之前版本,它的默认值是1,在8i及以后版本,默认值是0。一般设置为1-(65536/DB_BLOCK_SIZE)。
     在9i中,这个参数是一个隐藏参数:_HASH_MULTIBLOCK_IO_COUNT,可以通过表x$ksppi查询和修改。
     另外,在MTS中,这个参数将不起作用(只会使用1)。
     它的最大值受到OS的IO带宽和DB_BLOCK_SIZE的影响。既不能大于MAX_IO_SIZE/DB_BLOCK_SIZE。
     在8i及以后版本,如果这个值设置为0,则表示在每次查询时,Oracle自己自动计算这个值。这个值对IO性能影响非常大,因此,建议不要修改这个参数,使用默认值0,让Oracle自己去计算这个值。
     如果一定要设置这个值,要保证以下不等式能成立:
     R/M < Po2(M/C)
     其中,R表示小表的大小;M=HASH_AREA_SIZE*0.9;Po2(n)为n的2次方;C=HASH_MULTIBLOCK_IO_COUNT*DB_BLOCK_SIZE。


HASH JOIN 的过程:

Hash join算法的一个基本思想就是根据小的row sources(称作build input,我们记较小的表为S,较大的表为B) 建立一个可以存在于hash area内存中的hash table,然后用大的row sources(称作probe input) 来探测前面所建的hash table。(这里的小表和大表并不是依据表的物理大小,而是依据在当前query条件下,所查询出的数据的大小)。

第1步:对小表数据进行分区,分区的大小满足如下条件:  
        (Number of Partitions) * C<= Favm *M 
        其中C为Cluster size, 其值为DB_BLOCK_SIZE*HASH_MULTIBLOCK_IO_COUNT;Favm为hash area内存可以使用的百分比,一般为0.8左右;M为Hash_area_size的大小。 
第2步:读取部分小表S数据,对每一条数据计算其hash1和hash2的值,hash1、hash2分别采用不同的hash算法获得,hash1主要用于将记录和分区对应,而hash2主要将记录和bucket对应,每个分区下含有多个bucket。创建hash table是依据hash2的。

第3步,依据hash值在更新位图向量,将对应bucket的标识位置为1

  第4步:将记录写入对应分区,如果内存不够,则将内存中数据量最大的分区写入硬盘,如果在此之前分区已经写入硬盘,则直接将该记录写入硬盘上的对应分区,写入的数据包括select中的列和hash2。 
第5步:读取小表S的剩余部分,2-4,直至小表S全部读完。 
第6步:将所有S的分区,按照数据量大小进行排序,然后按照从小到大的顺序读入分区置内存,从而使内存中可以包含最多的分区。
第7步:依据hash2建立hash table。 


第8步:读取大表b的记录,计算hash1和hash2,通过hash2查询位图向量,如果对应的标识位为0,则丢弃该记录,否则继续(这种做法称为“位图过滤”)。 
第9步:对通过过滤的数据依据hash1将记录映射到相应的分区中去,如果分区在内存中,则通过hash2与hash table链接,查询对应的表1记录,并将连接结果写入硬盘或者返回给客户端。如果对应分区在硬盘中,则将该记录写入与小表分区对应的大表分区。 
第10步:继续读取大表,直至表B读取完毕。

第11步:对存储在硬盘中的小表分区和大表分区做hash 链接,这时并不一定以小表的分区为驱动表,而是选择两个分区中的数据量较小的分区为驱动分区,这种机制称为“动态角色互换” 。
第12步:重复11,直至所有分区处理完毕。

注意:如果某个驱动分区过大,无法再内存中装入,oracle会分批次读入该分区的部分数据,建立hash table,没一部分驱动数据均会与探测分区的全部数据进行匹配,从而造成探测分区的多次读入,导致性能的下降。这称为 nested-loop hash join. 如果有nested-loop hash join发生,我们可以尝试降低HAHS_MULTIBLOCK_IO_COUNT的取值,从而增加分区数量来解决这个问题。



三种模式:

optimal:此时hash_area_size的大小,可以完全装载驱动表,因此,不会有分区写入到硬盘,此时效率最高。
  onepass:此时hash_area_size的大小可以容纳单个或多个分区,但是无法装载全部驱动表,因此,部分分区写入硬盘,导致效率降低。

multipass :此时hash_arem_size的大小不足以装载单个分区的大小,因此,会产生 nested-loop hash join,这是最复杂,最糟糕的hash join,性能会急剧下降。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值