从数据库的聚合到shuffle

2 篇文章 0 订阅
离职前的最后一天,又是带薪抽烟带薪划水的一天

前言:数据库怎么实现聚合?看过《数据库系统实现》第四章的话,知道有基于散列和基于排序的2种方式,散列理解成hash即可,散列和排序很容易联想到mr的shuffle和spark的shuffle,对于像我这样的初学者来说,知道为什么比知道怎么做的更重要,所以本篇只是很肤浅的介绍下它们之间实现的异同。

数据库怎么实现聚合?

如下表A,2个字段,userid和sal

b1
a3
b4
a7
c5

怎么实现select userid,sum(sal) from A group by userid?

1.基于hash

先聚合再func

创建一个hash表,key为groupby的userid,v为list(sals),scanA,

读<b,1>  hash表为(<b,list(1)>)

读<a,3>  hash表为(<b,list(1)>,<a,list(3)>)

读<b,4> hash表为(<b,list(1,4)>,<a,list(3)>)

读<a,7> hash表为(<b,list(1,4)>,<a,list(3,7)>)

读<c,5>hash表为(<b,list(1,4)>,<a,list(3,7)>),<c,list(5)>)

然后再执行func(list()),这里的func为sum,结果为(<b,5>,<a,10>,<c,5>)至此我们就实现了简单的聚合

边聚合边func

可以看到这样的方式先根据k聚合到一起,然后再在集合上执行func,func前聚合的阶段会比较耗内存,因为中间结果占了资源,而这些中间结果是最终结果不需要的,所以可以聚合的时候就执行func online聚合,改进流程如下

读<b,1>  hash表为(<b,1>)

读<a,3>  hash表为(<b,1>,<a,3>)

读<b,4>  因为hash表已存在k,所以我们先取出hash表b对应 1,然后和<b,4>执行sum,并将5更新hash表

所以hash表为(<b,5>,<a,3>)

继续读<a,7> ,同上,hash表为(<b,5>,<a,10>)

读<c,5>hash表为(<b,5>,<a,10>,<c,5>)

以上我们讨论的都是内存充足的情况,当内存不足的时候,《数据库系统实现》第四章指出三种算法实现,一趟、两趟直到多趟,下面我们来讨论内存不足的情况下基于hash是否可行?虽然已经有好多资料指出内存不足基于hash不太可行

内存不足情况下的hash

还是以上述6条为例,为了简化模型,假设内存只能容纳2条,1条占一个块(或者页亦或者别的啥专业术语),也想整点通俗易懂的图,奈何自己不会画

读<b,1>  hash表为(<b,list(1)>)

读<a,3>  hash表为(<b,list(1)>,<a,list(3)>)此时内存已满,2个块刷回磁盘

读<b,4> hash表为(<b,list(4)>,)

读<a,7> hash表为(<b,list(4)>,<a,list(7)>)此时内存已满,2个块刷回磁盘

读<c,5>hash表为(<c,5>)至此我们读了5个块,O(n),

接下来我们该怎么做呢?

遍历,遍历所有写回磁盘的块,一个块一个块的读,与在内存中的(<c,5>) compare,判读k是否相等,相等则更新内存中的(<c,5>),并将相等的写回一个地方A区,不相等写回一个地方B区,因为相等的A区后续不会再读了,而不相等的后续仍需遍历,以这次的<c,5>为例,遍历完另外4条后,仍需写回磁盘B,遍历完磁盘中的4个块后,将结果(<c,5>)写磁盘或到输出缓冲区

此时我们先读B区的<b,list(1)>,遍历B区的剩余3条,读到k为a的,写回B区,k为b时,更新结果<b,5>

虽说上述的场景很简单,也可看出来,极端情况下,以唯一值聚合,O(n^2)每取出一条都会去遍历B区的数据,类似嵌套循环join,当然如果有索引的话,这个流程会快很多,下面我们讨论基于排序的聚合

2.基于排序

我们假设数据本身有序,比如b树家族,或者聚合键上有索引,可以遍历索引得到有序的数据,亦或者数据无序,我们可以先排序,内存不足使用外部归并排序,具体就不展开了,不是该篇重点

数据本身有序的情况下,读一条,继续按序再读一条,如果k相等,聚合,k不相等,第一条就可以输出了,紧接着读第三条和第二条比较,同理,类似sortmergejoin,所以数据本身有序的情况下,遍历一遍即可得到最终聚合后的结果

排序vs哈希

哈希内存充足的情况下,O(n),遍历一遍,内存不足,最坏的情况是0(n^2),读一条,遍历所有剩下的,最好的情况下,n条数据,k相等,也是O(n),数据本身有序,足不足都是0(n),数据无序就看用的什么排序算法了

为什么要shuffle、不shuffle行不行?

olap常用的基础sql操作符groupby+join

select field1,聚合函数 from table group by field1

select xxx from tableA  A join tableB  B on A.关联字段=b.关联字段

下面我们讨论在分布式的环境下怎么实现上述2个基本操作

1.聚合+groupby

忽略副本,加入副本的话会变的比较复杂,所以忽略高可用,就2个节点,

节点A:<a,3>、<b,5>、<a,6>、<c,2>

节点B:<a,1>、<b,2>、<c,3>、<a,4>

表一部分数据在节点A,一部分在节点B

select id,count(1) from group by id

方式1

节点A、B都执行上述sql,然后汇总

节点A结果:<a,2>、<b,1>、<c,1>

节点B结果:<a,2>、<b,1>、<c,1>

最后将2个节点的结果拉到driver汇总,<a,4>、<b,2>、<c,2>

缺点:大量中间数据会拉到driver端汇总,driver容易崩,上述事例中,A、B节点的3条数据都会到driver端

方式2

改进方式1,减轻driver端压力,先把相同id的拉到同一节点,再执行聚合函数,最后汇总

相同id拉到同一节点就是shuffle,现在不讨论怎么拉到相同节点,后续怎么实现Shuffle讨论

每个id计算hash(id)%2,值为0拉到节点A,值为1拉到节点B,假设a、b拉到节点A,c拉到节点B

第一步,A、B节点互相拉取自己的数据

A:<a,3>、<b,5>、<a,6>、<a,1>、<b,2>、<a,4>

B:<c,2>、<c,3>

第二步,两个节点数据执行聚合函数

A:<a,4>、<b,2>

B:<c,2>

第三步,汇总driver返回结果

与方式1相比,driver端只拉取了3条数据,大大减轻了压力

优点:减轻了driver压力,缺点A、B互拉自己数据时有大量中间数据,对网络有比较大的压力

方式3

先每个节点聚合,再互拉自己数据,再每个聚合,最后汇总

第一步,先聚合

A:<a,2>、<b,1>、<c,1>

B: <a,2>、<b,1>、<c,1>

第二步,拉取自己数据

....

这样我们就可以减轻网络的压力,这就是map端聚合,而互拉自己数据就是shuffle

对于聚合+groupby:shuffle可以减轻driver压力,map端聚合可以减轻网络IO

2.join

节点A:表1 <a,2>,<b,3>   表2 <c,1>,<b,3>

节点B:表1 <c,2>,<a,3>   表2 <a,1>,<a,3>

接下来我们讨论怎么实现join   select xxx from 表1 join 表2 on 1.id=2.id

可以按照groupby的思路先单节点关联再汇总

节点A结果:<b,(1_3,2_3)>#_前的代表表别名

节点B结果:<a,(1_3,2_1)>,<a,(1_3,2_3)>

可以看出来,先单节点关联不是我们想要的结果,所以还是先互拉自己数据再关联

接下来我们讨论到底怎么互拉自己数据,并且为了减少网络io怎么实现map端聚合

怎么实现shuffle?

对于每个节点的kv,先根据k计算pid(后续哪个节点来拉取这条数据),为了简化shuffle,A节点只从B节点拉一次数据,不涉及多核多task

方式1:

对于每个节点上面的kv,计算完pid,输出到对应缓冲区,预先起对应节点数量的缓冲区,比如A节点,起2个,一个是自己的,一个是属于节点B的,缓冲区满了就刷写文件,直到所有kv计算完pid,然后节点B拉取属于自己的文件

缺点:上述事例比较简单,实际的场景中,后续会有几百几千个task,会起同样数量的缓冲区,会占用大量内存,而且该方式没有map端聚合,网络压力大,临时文件巨多

方式2:

为了解决临时文件多的问题,我们可以后台起任务来合并属于相同节点的文件

为了实现Map端的聚合,我们可以参考第一部分数据库怎么实现聚合,一种hash,一种排序,先说基于排序的

方式3:

我去越写越长,后面就简单点写吧

 

 

hadoop、spark怎么实现shuffle?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值