赞
踩
大数据时代,日常工作中经常会处理数以亿计的数据。
笔者近期就遇到了一个十亿级以上的数据排序需求,并输出序号。
如果是小规模数据我们直接使用row_number全局排序就可以了,但是当数据规模达到十亿或者以上时,直接使用row_number肯定是不太现实。
因为全局排序的时候变成了单节点任务,要么超内存,要么就超时。
经过几轮调试,问题解决了,并且性能还不错,笔者把处理这个问题的思路与解决方案分享出来与网友交流学习。
目标全局排序15亿条记录,排序字段是一个概率分(分值在0-1之间)。
由于直接使用row_number肯定不能解决。大数据处理还是需要分布式的思想,笔者首先想到的是根据数据大小对数据进行分段排序,然后在合并逐段的排序结果形成全局排序结果(归并排序的一种特殊形式)。
因为有15亿数据,分成1000段,平均每个reduce处理150W。
想好了,直接写代码开干。
with t1 as ( select id,p,ceil(p*1000) as sub_level, row_number() over(partition by ceil(p*1000) order by p) as sub_order from table_source ), t2 as ( -- 获取每个分桶的起始排序(使用sum over累加计算得出的当前分桶与比他小的分桶累计了多少条记录) select sub_level,(sum(cnt) over(order by sub_level) - cnt) as base_order from ( -- 获取每个分桶的数量 select sub_level,count(1) as cnt from t1 group by sub_level ) t ) -- 起始排序 + 组内排序就是全局排序 select id,p,base_order + sub_order as global_order from t1 left join t2 on t1.sub_level = t2.sub_level ;
由于数据分布不均匀,上面的代码运行直接部分节点爆内存了。
那么就粗暴的加大分桶数量直接到5000(把上面代码中的1000修改成5000),再运行就成功了。
但是耗时4800s。相对较长,分析发现主要是分桶不均匀导致长尾效益。
这种粗暴的分桶方式对于数据分布比较均匀的场景比较适合,但是对于数据分布不均匀的场景可能就比较低效。
那么我们该怎么优化一下呢?
由于问题是数据分桶不均匀导致的,那么我从分桶均的角度来解决。
select ceil(p*1000) as sub_level, count(1)
from table_source
group by ceil(p*1000)
order by 2 desc;
根据结果发现数据在四个区间分布比较
(0,0.05] 分布占大部分
(0.05,0.056]分布比较密集
(0.057,0.061]分布高度密集
(0.061,0.15)分布较少
我们根据这个4个区间的数据量分布进行定值化分段,让分桶尽量均匀。
定制化分段后的sql如下,
with t0 as ( select id, p, case when p <= 0.05 then ceil(600 * (p-0.0) / (0.05-0.0)) when p <= 0.056 then 600 + ceil(400 * (p-0.05) / (0.056-0.05)) when p <= 0.061 then 800 + ceil(200 * (p-0.056) / (0.061-0.056)) else 1201 end as sub_level from table_source ), t1 as ( select id,p,sub_level, row_number() over(partition by sub_level order by p) as sub_order from t0 ), t2 as ( select sub_level,(sum(cnt) over(order by sub_level) - cnt) as base_order from ( select sub_level,count(1) as cnt from t1 group by sub_level ) t ) select id,p,base_order + sub_order as global_order from t1 left join t2 on t1.sub_level = t2.sub_level
这个sql相对之前粗暴分段的方式数据分布均匀多了,性能也大大提升。耗时不到原来的一半。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。