項目中經常會使用到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,改變加載規則