项目中经常会使用到Spark进行批处理,数据量大的时候总是会遇到数据倾斜的情况,参考了项目中遇到的情况以及网上的一些案例,写篇文章来总结下如何处理Spark中常见的数据倾斜问题。当然由于业务场景不一样,本文说到的一些解决数据倾斜的思路肯定不全,如果有更好的方法,方便的话在评论里也告诉我一下哈。
啥叫数据倾斜:
Spark的RDD由多个Partition组成,如果某个Partition的数据比其他Partition的数据要多很多,这个就是数据倾斜,如下图所示:
数据倾斜会导致某个spark任务耗时过长,导致整体任务耗时增加,甚至可能造成OOM。
数据倾斜大概率是由于HashPartitioner引起的,Range不会,具体可以看我之前写的《HashPartitioner 与 RangePartitioner》那篇文章。
数据倾斜为什么会造成OOM:
在之前分析Shuffle的时候说过,ShuffleWrite端使用的数据结构PartitionedAppendOnlyMap、PartitionedPairBuffer以及ShuffleReader端使用的ExternalAppendOnlyMap、ExternalSorter等等,都会检查缓存数据的大小,如果太大就会讲数据刷写到磁盘,即理论上应该不会出现OOM。
个人认为是由于Spark并非来一条数据就计算一次内存使用大小,那样太费性能了。Spark使用的是按等比的采样数量来估算大小,比如第一条、第二条、第四条、第八条..这样很容易造成估计的内存大小不准确,从而造成OOM。
数据倾斜如何解决:
1. 过滤掉不用的Key
有些Key是脏数据,直接过滤掉,这样可以减少数据量
2. 调整并行度
增大Partition的数量(算子中指定partition数量或者使用reparation算子,不建议自定义partitioner),这样每个Task要处理的任务就少了,各个key可以均匀的分到多个Partition中。但是如果某个Key的数量就是很多,就不能改变这种问题...
3.将Reduce side Join转变为Map Side Join
Spark SQL中自动就有这样的优化,我们可以借鉴下!
通过较小的数据的Broadcast出去,将Reduce侧Join转化为Map侧Join,避免Shuffle从而完全消除Shuffle带来的数据倾斜,这样shuffle都不会发生,贼6。(ps: 建议groupByKey算子改为reduceByKey,让map端也发生agg)
4. 随机前缀扩容 — 进行分阶段聚合
Key之前打个随机数,然后聚合,在调用算子,这样可以较少数据量,进行业务计算之后,再进行第二阶段的聚合。
不过得根据业务需求视情况来定,业务接受Key可以分散计算才行。
5. 如果加载数据的时候就发生了数据倾斜,比如使用Spark加载HBase的数据,有些Region的数据量大,有些Region的数据量少。
可以在加载完之后Region之后进行reparation,或者重写TableInputFormat,改变加载规则