hybrid hash join with skew

这算是《数据库系统实现》和interdb.jp的读书笔记。

这个hash join和字面理解的不太一样,书中分类关系代数算法,是按一趟两趟IO这种分类的。

而hash join有一趟两趟之分,一趟就是把其中整个表装入内存,生成hash表,然后和另个表做join。

而hash join两趟算法,又分为普通的hash join、hybrid hash join和hybrid hash join with skew(PostgreSQL),其实,书中的两趟hash算法是许多算法的基础,不止用于join。

1、两趟hash join大体思想:

以JOIN属性作为输入,用hash函数对JOIN的两个表进行分区(桶),两个表使用同样的hash函数。

这些桶里的数据会被写到磁盘,是文件的形式,当两个表hash到同一个桶时,可以保存到一个文件里,也可以分别存到两个文件里。当把两个表都完成分桶,并且都从新写入文件后(注意,完成了两个表的读写IO各一次),再每次从磁盘读取两个表对应的桶文件,进行JOIN,因为hash函数的特点,如果两个表JOIN属性相等,则相等的属性必然都在同一桶(分区)内。

相对于nestloopjoin,不必以一个表的一个记录,遍历另一个完整的表,大大减少了计算和IO量。

这里需要注意的是桶的大小,由于可以用hash函数结合取余,可以有任意多个的桶(分区),我们需要设计,使工作内存区域能一次装下整个分区,否则要对分区再分。

内存能装下整个分区,就可以在内存里构建完整的hash表,使用前面一趟的hash join算法。

至于桶的大小的计算,书中想的很细,把内存分为一个一个块,与磁盘中的块对应,这种思维便于对内存和磁盘进行数学建模。

2、两趟hybrid hash join大体思想:

两趟hybrid hash join在前面的算法基础上有一点点改进,就是对第一个表分区的时候,第一个表的第一个分区留在内存,不写到磁盘,因为计算写到磁盘,稍后还要从磁盘读到内存,于是干脆留在内存,节省了IO,但是第一个表的其它分区还是要写到磁盘的,因为内存不够,然后第二个表做hash分区时,对于每个tuple,先看看是不是hash映射到第一个表的第一个分区(驻留在内存的),如果是,尝试与这个分区里的记录JOIN(具体怎么JOIN就简单了),如果JOIN成功就输出,JOIN不成功就抛弃。如果没有hash映射到第一个表的第一个分区,就去到其它分区并写磁盘。

把第二个表过了一遍后,第二个表的记录,要么与第一个表第一个分区中记录JOIN并输出,要么映射到那个分区但是没有匹配的记录,则抛弃,要么映射到其它分区并存盘,此时内存中第一个表的第一个分区完成使命,不再有用了,可以删除了,然后加载第一个表的第二个分区,之后的算法就与两趟hash join相同了。

这个算法的改进就是,让第一个分区的记录尽量的多,当前前提是内存能一次性装下。

3、两趟hybrid hash join with skew大体思想:

这又是对前一种算法的一点点改进,从hybrid hash join的过程可以看到,对于第二张表的第一次遍历,处理掉了第二张表的部分记录和第一张表的第一个桶,这些记录,要么被作为结果输出,要么被抛弃,不会出现在后面的计算中。

可见,第一次遍历,处理掉的记录越多越好。

有没有办法,尽量增大第一次遍历处理掉的记录数呢?可以使用JOIN条件中列的统计数据,例如:join on t1.y = t2.y,如果t2是第二个表,即外表,并且我们知道t2的记录在y列值的分布,那就选占数量最多的那几个y值,例如,如果t2的y列值的分布有这样的特点:假如y是整数,取值为1、3、5三个数的记录占了70%,此时就可以用这种改进算法。

在t1第一次遍历时,除了在内存中保留第一个分区,t1中y值为1、3、5的记录也被保留在内存中形成skew分区(组织成查询结构),当然这两个分区的记录不能有重复,即y值为1、3、5的记录不再被hash分区,然后在对t2遍历时,先看看t2的记录能不能和skew分区join(按照铃木的说法,如果这条记录y的MCV较大就和skew分区join),如果能join并且match就算被处理掉了,如果不能就hash一下,看是不是在t1的第一个分区,如果不是就存磁盘后面处理,如果在t1的第一个分区,就在这里处理掉。

按照前面的假设,t2的70%的记录都可以和skew分区join并且match,就被处理掉了,t2经过第一次遍历后,t1的第一个分区和skew分区都可以抛弃掉,接下来按照常规两趟hashjoin处理的数据就很少了。

4、这个统计数据或者MCV是个什么鬼?

MCV全称为Most Common Value。在PG的pg_stats中,一个列的统计数据是一条记录,这条记录包含两个字段:most_common_vals和most_common_freqs,第一个字段,是这个列中出现次数最多的几个值,第二个字段,是这几个值的记录数占整个表记录数的百分比。

例如下面是某个表continent列的统计数据:

-[ RECORD 1 ]-----+-------------------------------------------------------------
most_common_vals  | {Africa,Europe,Asia,"North America",Oceania,"South America"}
most_common_freqs | {0.274611,0.243523,0.227979,0.119171,0.0725389,0.0621762}

可见值为“Africa”的记录最多,约占27%,其次是值为“Europe”的记录,约占25%

对于hybrid hash join with skew,加入JOIN条件是continent,而外表(第二个表)的continent列统计数据如上,则可以把t1中continent列值为Africa,Europe,Asia,"North America"的记录取出来组成skew分区,因为y为这四个值的记录,在外表中占了99%。

当然,考虑到这个skew分区必须能全部被装入内存,如果从t1中选y值为四个值的记录,导致skew太大,不能被装入内存,就只能尝试只选两个或一个MCV了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值