赞
踩
假设spark中存在这样的一张存放着用户粉丝数的表 user_fan,字段为用户id–userId,粉丝id --fanId,现在要求出每个用户的粉丝数目,我们有以下的sql:
select userId,count(1) as cnt from user_fan group by userId
我们知道用户的粉丝数目是不均匀的,有些用户拥有上千万粉丝,而有些用户只有几十粉丝,这样一个sql带来的结果就是数据量大的分区的task花费很长时间,而数据量小的分区只需要花费很短的时间,当然如果每个task分区的时候恰巧能够分的均匀,比如A,B用户有100w粉丝,C,D用户有10个粉丝,如果分区时A和C用户分到同一个分区,B和D用户分到同一个分区,那么此时不会产生问题,但是这只是巧合,大部分情况下,分区时按照文件大小分区的,他不会理会业务的含义,所以这就造成了A,B,C在同一个分区,D在另一个分区,这样数据就会非常不均匀,所以我们这里能做的就是尽量让每个分区更加均匀,那怎么做到呢,我们知道按照用户维度来分组,肯定是不能达到目的的,那怎么做呢?
我们要做的就是分而治之的方法:
实现一:
select userId,count(1) as cnt from
(select userId,fanMod,count(1) as ufcnt from
(select userId,fanId,fanId % 10 as fanMod from user_fan) as a group by userId,fanMod) as b group by userId
先按照userId + fanMod的方式分组,其中fanMod我们可以取10或者100等,这样基于userid+fanMod的分组方式每一组数据就差不多均匀了,这样第一阶段得到userId+fanMod对应的count表的各个task运行时间就是均匀的,可以充分利用并行cpu加速.然后第二阶段再获取userId对应的count表,在这一个阶段中对于有大量粉丝的用户来说对应的fanMod数量也就是比如就是10或者100,对于小粉丝的用户来说可能就是1,但是由于这个数量差距很小(不会有类似之前sql用户粉丝间上百万的差距),所以第二阶段的task运行时速度不会相差很大,这样就达到了分而治之的目的.
实现二:
实现二的思路是先把用户粉丝大的记录分成一组,粉丝数量小的用户记录分成另一组,当前前提是你提前知道哪些用户的粉丝数量大,哪些用户的粉丝数量比较小,当然数量不需要太精确,然后对于每一组聚合求每个用户的粉丝数量,由于在每一组中每个用户的粉丝数量都在一个量级上,所以不会导致数据量的倾斜,这样每个组都获取到结果之后再把每一组的结果合并起来得到最终的结果.
--大粉丝组
select userId,count(1) as cnt from user_fan where user in (大粉丝组) group by userId
union
--小粉丝组
select userId,count(1) as cnt from user_fan where user in (小粉丝组) group by userId
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。