写在前面: 我是
「nicedays」
,一枚喜爱做特效,听音乐,分享技术的大数据开发猿
。这名字是来自world order乐队的一首HAVE A NICE DAY
。如今,走到现在很多坎坷和不顺,如今终于明白nice day是需要自己赋予的。
白驹过隙,时光荏苒,珍惜当下~~
写博客一方面是对自己学习的一点点总结及记录
,另一方面则是希望能够帮助更多对大数据感兴趣的朋友。如果你也对大数据与机器学习
感兴趣,可以关注我的动态https://blog.csdn.net/qq_35050438
,让我们一起挖掘数据与人工智能的价值~
文章目录
Spark数据倾斜六大使用场景:
一:倾斜表现:
- executor lost,OOM,shuffle过程出错
- driver oom
- 单个executor执行时间特别久,整体任务卡在某个阶段不能结束
- 正常运行的任务突然失败
二:倾斜产生原因:
一般都是shuffle过程中产生
- key分布不均匀
- 建表时考虑不周
- 业务数据激增
三:解决数据倾斜思路:
方案一:Hive ETL预处理数据
实现思路:
并没有本质解决数据倾斜,只是让数据倾斜的发送提前HiveETL上,提前进行离线shuffle。
应用场景:
系统主要是允许用户通过Java Web系统提交数据分析统计任务,后端通过Java提交Spark作业进行数据分析统计。
要求 Spark 作业速度必须要快,尽量在 10 分钟以内,否则速度太慢,用户体验会很差。所以我们将有些 Spark 作业的 shuffle 操作提前到了 Hive ETL 中,从而让 Spark 直接使用预处理的 Hive 中间表,尽可能地减少 Spark 的 shuffle 操作,大幅度提升了性能,将部分作业的性能提升了 6 倍以上.
方案二:过滤倾斜数据
当少量key重复次数特别多时,考虑到该key是否有用处,无用可以直接filter
实现思路:
动态的sample算子对RDD采样,对数据量最多的key,用sparksql的where或者RDD的filter过滤掉。
应用场景:
有一次发现某一天 Spark 作业在运行的时候突然 OOM 了,追查之后发现,是 Hive 表中的某一个 key 在那天数据异常,导致数据量暴增。因此就采取每次执行前先进行采样,计算出样本中数据量最大的几个 key 之后,直
接在程序中将那些 key 给过滤掉。
方案三:提高shuffle操作的并行度:
最简单的方案
实现思路:
给reduceByKey(1000)多设置并行参数,默认是200,对于很多场景都太小了。
应用场景:
该方案通常无法彻底解决数据倾斜,因为如果出现一些极端情况,比如某个 key 对应的数据量有 100 万,那么无论你的 task 数量增加到多少,这个对应着 100 万数据的 key 肯定还是会分配到一个 task 中去处理,因此注定还是会发生数据倾斜的。所以这种方案只能说是在发现数据倾斜时尝试使用的第一种手段,尝试去用最简单的方法缓解数据倾斜而已,或者是和其他方案结合起来使用
方案三:引入随机数–两段聚合
groupby分组时,同值key被分到同组,在一个task上运行,此时同组某key明显多于其他组,造成的数据倾斜。
实现思路:
是对该key值增加随机数或者hash加盐,使同组key刚开始groupby时被均匀分到多个task运行先进行一次聚合操作,然后再将随机数去除,使得原本key值再次分到同一组,此时key值由于上一轮的聚合,数量变得少了很多,然后再二次聚合。
应用场景:
仅仅适用于聚合类的 shuffle 操作,适用范围相对较窄。如果是 join 类的 shuffle 操作,还得用其他的解决方案
方案四:将reduce join 转为map join
在对 RDD 使用 join 类操作,或者是在 Spark SQL 中使用 join 语句时,而且 join 操作中的一个 RDD 或表的数据量比较小(比如几百 M 或者一两 G),
实现思路:
不使用 join 算子进行连接操作,而使用 Broadcast 变量与 map 类算子实现 join 操作,进而完全规避掉 shuffle 类的操作,彻底避免数据倾斜的发生和出现。将较小 RDD 中的数据直接通过 collect 算子拉取到 Driver 端的内存中来,然后对其创建一个 Broadcast 变量,广播给其他 Executor 节点;
接着对另外一个 RDD 执行 map 类算子,在算子函数内,从 Broadcast 变量中获取较小RDD 的全量数据,与当前 RDD 的每一条数据按照连接 key 进行比对,如果连接 key 相同的话,那么就将两个 RDD 的数据用你需要的方式连接起来。
应用场景:
适用场景较少,因为这个方案只适用于一个大表和一个小表的情况。毕竟我们需要将小表进行广播,此时会比较消耗内存资源,driver 和每个 Executor 内存中都会驻留一份小 RDD 的全量数据。如果我们广播出去的 RDD 数据比较大,比如 10G 以上,那么就可能发生内存溢出了。因此并不适合两个都是大表的情况。
方案五:采样倾斜key并拆分join操作
如果两个表数据量都很大呢,没有办法是用map join操作,那么观察两张表,如果一张表得少数key数据量过大,而另一张所有key分布均匀。
实现思路:
对少数几个数据量过大得key的RDD,通过sample算子采样,统计每个key的数量,找出数据量最大的几个key。
当这几个key对应得数据从原来得RDD中拆分出来,形成一个单独得RDD,给每个key都打上n以内随机数作为前缀。
同时把另一个需要join得RDD也过滤出来那几个key形成单独得RDD,将每条数据膨胀成n条,每个n条数据都按顺序加上0-n得前缀。
将附加了随机前缀的独立 RDD 与另一个膨胀 n 倍的独立 RDD 进行 join,此时就可以将原先相同的 key 打散成 n 份,分散到多个 task 中去进行 join 了。
而另外两个普通的 RDD 就照常 join 即可。
最后将两次 join 的结果使用 union 算子合并起来即可,就是最终的 join 结果
应用场景:
优点:对于 join 导致的数据倾斜,如果只是某几个 key 导致了倾斜,采用该方式可以用最有效的方式打散 key 进行 join。而且只需要针对少数倾斜 key 对应的数据进行扩容 n 倍。
不需要对全量数据进行扩容。避免了占用过多内存。
缺点:如果导致倾斜的 key 特别多的话,比如成千上万个 key 都导致数据倾斜,那么这种方式也不适合。
方案六:使用随机前缀和扩容RDD进行join
如果join操作,RDD中有大量得key导致数据倾斜
实现思路:
同理,先sample查看有哪些key数据量过大,发现有很多key都超过了万条数据。
将倾斜得RDD每条数据打上一个n以内得随机前缀。
同时对另一个RDD进行扩容,将每条数据都扩容成n条数据,扩容出来每条数据都依次打上0-n得前缀。
最后将两个处理后的 RDD 进行 join 即可
应用场景:
优点:对 join 类型的数据倾斜基本都可以处理,而且效果也相对比较显著,性能提升效果非常不错。
缺点:该方案更多的是缓解数据倾斜,而不是彻底避免数据倾斜。而且需要对整个 RDD进行扩容,对内存资源要求很高。