Spark學習——效能調優(三)

Hiway發表於2019-04-02

其他更多java基礎文章:
java基礎學習(目錄)


繼續上一篇Spark學習——效能調優(二)的講解

使用MapPartition提升效能

Spark學習——效能調優(三)

什麼時候比較適合用MapPartitions系列操作?

資料量不是特別大的時候,都可以用這種MapPartitions系列操作,效能還是非常不錯的,是有提升的。

在專案中,自己先去估算一下RDD的資料量,以及每個partition的量,還有自己分配給每個executor的記憶體資源。看看一下子記憶體容納所有的partition資料,行不行。如果行,可以試一下,能跑通就好。效能肯定是有提升的。

但是試了一下以後,發現,不行,OOM了,那就放棄吧。

filter過後使用coalesce減少分割槽數量

Spark學習——效能調優(三)
預設情況下,經過了這種filter之後,RDD中的每個partition的資料量,可能都不太一樣了。(原本每個partition的資料量可能是差不多的) 這可能會導致的問題:

  1. 每個partition資料量變少了,但是在後面進行處理的時候,還是要跟partition數量一樣數量的task,來進行處理;有點浪費task計算資源。
  2. 每個partition的資料量不一樣,會導致後面的每個task處理每個partition的時候,每個task要處理的資料量就不同,這個時候很容易發生資料傾斜。

針對上述的兩個問題,我們希望應該能夠怎麼樣?

  1. 針對第一個問題,我們希望可以進行partition的壓縮吧,因為資料量變少了,那麼partition其實也完全可以對應的變少。比如原來是4個partition,現在完全可以變成2個partition。那麼就只要用後面的2個task來處理即可。就不會造成task計算資源的浪費。(不必要,針對只有一點點資料的partition,還去啟動一個task來計算)

  2. 針對第二個問題,其實解決方案跟第一個問題是一樣的;也是去壓縮partition,儘量讓每個partition的資料量差不多。那麼這樣的話,後面的task分配到的partition的資料量也就差不多。不會造成有的task執行速度特別慢,有的task執行速度特別快。避免了資料傾斜的問題。

coalesce運算元
主要就是用於在filter操作之後,針對每個partition的資料量各不相同的情況,來壓縮partition的數量。減少partition的數量,而且讓每個partition的資料量都儘量均勻緊湊。

從而便於後面的task進行計算操作,在某種程度上,能夠一定程度的提升效能。

RDD.filter(XXX).coalesce(100);
複製程式碼

使用foreachPartition優化

Spark學習——效能調優(三)

Spark學習——效能調優(三)

使用repatition解決Spark SQL低並行度

前說過,並行度是自己可以調節,或者說是設定的。

1、spark.default.parallelism
2、textFile(),傳入第二個引數,指定partition數量(比較少用)
複製程式碼

官方推薦,根據你的application的總cpu core數量(在spark-submit中可以指定,比如 200個),自己手動設定spark.default.parallelism引數,指定為cpu core總數的2~3倍。400~600個並行度。

你設定的這個並行度,在哪些情況下會生效?哪些情況下,不會生效?

如果你壓根兒沒有使用Spark SQL(DataFrame),那麼你整個spark application預設所有stage的並行度都是你設定的那個引數。(除非你使用coalesce運算元縮減過partition數量)

問題來了,如果用Spark SQL,那含有Spark SQL的那個stage的並行度,你沒法自己指定。Spark SQL自己會預設根據hive表對應的hdfs檔案的block,自動設定Spark SQL查詢所在的那個stage的並行度。你自己通過spark.default.parallelism引數指定的並行度,只會在沒有Spark SQL的stage中生效。

比如你第一個stage,用了Spark SQL從hive表中查詢出了一些資料,然後做了一些transformation操作,接著做了一個shuffle操作(groupByKey);下一個stage,在shuffle操作之後,做了一些transformation操作。hive表,對應了一個hdfs檔案,有20個block;你自己設定了spark.default.parallelism引數為100。

你的第一個stage的並行度,是不受你的控制的,就只有20個task;第二個stage,才會變成你自己設定的那個並行度,100。

問題在哪裡?

Spark SQL預設情況下,它的那個並行度,我們們沒法設定。可能導致的問題,也可能沒什麼問題。Spark SQL所在的那個stage中,後面的那些transformation操作,可能會有非常複雜的業務邏輯,甚至說複雜的演算法。如果你的Spark SQL預設把task數量設定的很少,20個,然後每個task要處理為數不少的資料量,然後還要執行特別複雜的演算法。

這個時候,就會導致第一個stage的速度,特別慢。第二個stage,刷刷刷,非常快。

如何解決
repartition運算元,你用Spark SQL這一步的並行度和task數量,肯定是沒有辦法去改變了。但是呢,可以將你用Spark SQL查詢出來的RDD,使用repartition運算元,去重新進行分割槽,此時可以分割槽成多個partition,比如從20個partition,分割槽成100個。

然後呢,從repartition以後的RDD,再往後,並行度和task數量,就會按照你預期的來了。就可以避免跟Spark SQL繫結在一個stage中的運算元,只能使用少量的task去處理大量資料以及複雜的演算法邏輯。

return dataDF.javaRDD().repartition(1000);
複製程式碼

Spark學習——效能調優(三)

使用reduceByKey本地聚合

Spark學習——效能調優(三)

用reduceByKey對效能的提升:

  1. 在本地進行聚合以後,在map端的資料量就變少了,減少磁碟IO。而且可以減少磁碟空間的佔用。
  2. 下一個stage,拉取資料的量,也就變少了。減少網路的資料傳輸的效能消耗。
  3. 在reduce端進行資料快取的記憶體佔用變少了。
  4. reduce端,要進行聚合的資料量也變少了。

相關文章