Spark Repartition 使用

看到一些同學的Spark代碼中包含了很多repartition的操作,有一些不是很合理,非但沒有增加處理的效率,反而降低了性能。這里做一個介紹。

repartition 從字面的意思看是是對數據進行重新分區,所以是會對數據進行打散。很多時候大家會覺得任務執行比較慢的時候,就會增加repartition的數量,其實這個時候不一定能提高應用執行的速度。這個操作的使用還要具體問題具體分析。

官方解釋

repartition(partitionNums): Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network.
中文:通過創建更過或更少的分區將數據隨機的打散,讓數據在不同分區之間相對均勻。這個操作經常是通過網絡進行數打散。

原理圖

image.png

repartition源代碼

repartition源碼

 /**
   * Return a new RDD that has exactly numPartitions partitions.
   *
   * Can increase or decrease the level of parallelism in this RDD. Internally, this uses
   * a shuffle to redistribute data.
   *
   * If you are decreasing the number of partitions in this RDD, consider using `coalesce`,
   * which can avoid performing a shuffle.
   */
  def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
    coalesce(numPartitions, shuffle = true)
  }

從注釋可以看到以下兩個點

  • 1 repartition可以將分區的并行度增加,也可以將分區的并行度減少
  • 2 可以看到repartition調用了coalesce方法,并且傳入的shuffle參數是true。換句說話,就是無論分區數是增加還是減少都會執行shuffle操作。

前提

使用repartition 使得任務能夠并行執行的話,分配的core的數量一定要略微大于最大的分區數,才能使得所有的
task能夠并行執行。
這里舉個例子,比如當前RDD有100個分區,如果我分配的是50個core(spark.executor.instances * spark.executor.cores),那么同時執行的任務最多只有50。同一個操作需分成兩部分串行執行,。這個時候如果有一個節點有問題(并行度是50 -1*2 = 48),那么就需要再次申請資源,如果資源緊張,需要等待,如果申請的時候申請的是54個core,雖然看起來有點浪費,但是對于任務的穩定性而言,卻可能會有很大的提升。當然最好的視情況是分配102-110比較合理。

repartition適用場景

通過上面注釋的理解,結合工作中的一些實踐經驗,repartition一般有如下幾個比較常見的場景:

處理能力不夠

RDD單個分區數據量比較大,或者單個分區處理比較慢,都可以通過repartition進行操作,這個時候numPartitions自然是要比當前的分區數要大一些。
單個分區數據量比較大,這種case在讀取和處理hdfs文件的時候并不常見,因為默認spark讀取hdfs文件的分區數和hdfs文件的block的數量是一致的。這個mr的mapper數量類似。但是對于Spark Streaming 任務,如果是window窗口較長,就比較容易出現單個分區數據量較大的情況。
還有一種case,比如每個需要有外部的一些讀寫操作,這個時候大量數據在一個partition中,會因為外部存儲、單機處理能力,網絡等的性能瓶頸導致數據每個partition的數據處理變慢,舉個例子,往redis里寫數據,如果每個partition數據量特別大,如果使用的是redis的非批量的,基本每個數據需要一次數據請求,假設采用的是pipleline的形式,依然會因為數據量很大導致這個分區的寫入很慢,從而導致任務執行時間較長。這種問題在Spark Streaming的任務中影響比較大。這個時候通過repartition增加分區數量效果也會很明顯。

數據傾斜

數據傾斜,這個自然不不用說了,使用repartition可以將數據進行打散,避免傾斜導致的執行耗時不均勻的問題。雖然這種情況是完全可以減少任務執行的時間,但是對于數據量比價小的分區而言,很快就能處理完,此時如果executor空閑的話,其實是比較浪費資源的。所以如果是通過這個處理數據傾斜的話,建議開啟動態資源分配。另外使用reapartition處理數據傾斜的時候,最好提前統計一下key的分布,這個比較常見的方法就是:
1 如果是hive的數據

select  count(1)  from table group by key

2 如果是rdd中統計

val keyCount =rdd.map((k,v) =>(k,1)).reduceByKey((a,b) =>(a+b)).sortBy(_._2,false).take(100)
print keyCount

需要進行合并場景

本身需要進行分區合并的時候使用coalesce是最合適的,原因就在于coalesce默認shuffle是false,也就是說,如果100個分區 想要變成50個分區,這個時候coalesce并不會進行shuffle操作。進行coalesce的場景,數據分區很多,但是每個分區數較少,這個時候其實并不太影響執行效率,但是對于一些需要外部鏈接,或者寫入文件的場景來說,這是很浪費資源的。
需要使用shuffle進行分區數減少的場景,原始的rdd分區數據分散不是很均勻,比如 當前RDD是100個分區,想要合并成50個分區,但是100個分區總數據分布從10K-200M分配不均,這個時候為了方便下游數據處理,我們可以將數據進行shuffle形式的合并。

總結

repartition是spark開發中使用非常多的一個操作,而且使用的是否合理直接影響到任務執行的快慢以及成功與否。是否需要進行repartition,以及repartition的數量是一個需要開發的同學多方面綜合考慮的結果,并沒有一成不變的經驗。但是我這兒依然給出一個具體的思路以供參考:確定原始數據分區數量,以及分區大小,key的分布式,參考集群資源的是否充足以及資源使用分布(cpu和內存使用比例),然后綜合考慮。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容