Spark性能調優 Shuffle(二)

1.shuffle原理

什麼樣的情況下,會發生shuffle

spark中,主要是以下幾個算子:groupByKeyreduceByKeycountByKeyjoin,等等。

什麼是shuffle

groupByKey,要把分佈在集羣各個節點上的數據中的同一個key,對應的values,都給集中到一塊兒,集中到集羣中同一個節點上,更嚴密一點說,就是集中到一個節點的一個executor的一個task中。

然後呢,集中一個key對應的values之後,才能交給我們來進行處理,<key, Iterable<value>>reduceByKey,算子函數去對values集合進行reduce操作,最後變成一個valuecountByKey,需要在一個task中,獲取到一個key對應的所有的value,然後進行計數,統計總共有多少個valuejoinRDD<key, value>RDD<key, value>,只要是兩個RDD中,key相同對應的2value,都能到一個節點的executortask中,給我們進行處理。

       shuffle,一定是分爲兩個stage來完成的。因爲這其實是個逆向的過程,不是stage決定shuffle,是shuffle決定stage

reduceByKey(_+_),在某個action觸發job的時候,DAGScheduler,會負責劃分job爲多個stage。劃分的依據,就是,如果發現有會觸發shuffle操作的算子,比如reduceByKey,就將這個操作的前半部分,以及之前所有的RDDtransformation操作,劃分爲一個stageshuffle操作的後半部分,以及後面的,直到action爲止的RDDtransformation操作,劃分爲另外一個stage

         每一個shuffle的前半部分stagetask,每個task都會創建下一個stagetask數量相同的文件,比如下一個stage會有100task,那麼當前stage每個task都會創建100份文件;會將同一個key對應的values,一定是寫入同一個文件中的;不同節點上的task,也一定會將同一個key對應的values,寫入下一個stage,同一個task對應的文件中。

shuffle的後半部分stagetask,每個task都會從各個節點上的task寫的屬於自己的那一份文件中,拉取key, value對;然後task會有一個內存緩衝區,然後會用HashMap,進行key, values的匯聚;(key ,values)

task會用我們自己定義的聚合函數,比如reduceByKey(_+_),把所有values進行一對一的累加;聚合出來最終的值。就完成了shuffle

shuffle前半部分的task在寫入數據到磁盤文件之前,都會先寫入一個一個的內存緩衝,內存緩衝滿溢之後,再spill溢寫到磁盤文件中。

2.shuffle的map端的文件合併

2.1. 默認的這種shuffle行爲,對性能有什麼樣的惡劣影響

實際生產環境的條件:

100個節點(每個節點一executor):100executor

每個executor2cpu core

總共1000task:每個executor平均10task

每個節點,10task,每個節點會輸出多少份map端文件?10 * 1000=1萬個文件

總共有多少份map端輸出文件?100 * 10000 = 100萬。

第一個stage,每個task,都會給第二個stage的每個task創建一份map端的輸出文件

第二個stage,每個task,會到各個節點上面去,拉取第一個stage每個task輸出的,屬於自己的那一份文件。

所以shuffle中的寫磁盤的操作,基本上就是shuffle中性能消耗最爲嚴重的部分。

2.2.開啓合併文件功能 

new SparkConf().set("spark.shuffle.consolidateFiles", "true")

開啓shuffle map端輸出文件合併的機制;默認情況下,是不開啓的,就是會發生如上所述的大量map端輸出文件的操作,嚴重影響性能。

3.Map端內存緩衝與reduce端內存佔比調節

3.1.map端內存緩衝

默認情況下,shufflemap task,輸出到磁盤文件的時候,統一都會先寫入每個task自己關聯的一個內存緩衝區。這個緩衝區大小,默認是32kb。每一次,當內存緩衝區滿溢之後,纔會進行spill操作,溢寫操作,溢寫到磁盤文件中去

3.2.reduce端內存佔比

reducetask,在拉取到數據之後,會用hashmap的數據格式,來對各個key對應的values進行匯聚。針對每個key對應的values,執行我們自定義的聚合函數的代碼,比如_ + _(把所有values累加起來)。reduce task,在進行匯聚、聚合等操作的時候,實際上,使用的就是自己對應的executor的內存,executorjvm進程,堆),默認executor內存中劃分給reduce task進行聚合的比例,是0.2

問題來了,因爲比例是0.2,所以,理論上,很有可能會出現,拉取過來的數據很多,那麼在內存中,放不下;這個時候,默認的行爲,就是說,將在內存放不下的數據,都spill(溢寫)到磁盤文件中去。

3.3.調優

默認,map端內存緩衝是每個task32kb

默認,reduce端聚合內存比例,是0.2,也就是20%

如果map端的task,處理的數據量比較大,但是呢,你的內存緩衝大小是固定的。可能會出現什麼樣的情況?

每個task就處理320kb32kb,總共會向磁盤溢寫320 / 32 = 10次。

每個task處理32000kb32kb,總共會向磁盤溢寫32000 / 32 = 1000次。

       在map task處理的數據量比較大的情況下,而你的task的內存緩衝默認是比較小的,32kb。可能會造成多次的map端往磁盤文件的spill溢寫操作,發生大量的磁盤IO,從而降低性能。

        reduce端聚合內存,佔比。默認是0.2。如果數據量比較大,reduce task拉取過來的數據很多,那麼就會頻繁發生reduce端聚合內存不夠用,頻繁發生spill操作,溢寫到磁盤上去。而且最要命的是,磁盤上溢寫的數據量越大,後面在進行聚合操作的時候,很可能會多次讀取磁盤中的數據,進行聚合。

       默認不調優,在數據量比較大的情況下,可能頻繁地發生reduce端的磁盤文件的讀寫。

這兩個點之所以放在一起講,是因爲他們倆是有關聯的。數據量變大,map端肯定會出點問題;reduce端肯定也會出點問題;出的問題是一樣的,都是磁盤IO頻繁,變多,影響性能。

       調節map task內存緩衝:spark.shuffle.file.buffer,默認32k(spark 1.3.x不是這個參數,後面還有一個後綴,kbspark 1.5.x以後,變了,就是現在這個參數)

      調節reduce端聚合內存佔比:spark.shuffle.memoryFraction,0.2

3.4. 在實際生產環境中,我們在什麼時候來調節兩個參數?

      看Spark UI,如果你的公司是決定採用standalone模式,那麼狠簡單,你的spark跑起來,會顯示一個Spark UI的地址,4040的端口,進去看,依次點擊進去,可以看到,你的每個stage的詳情,有哪些executor,有哪些task,每個taskshuffle writeshuffle read的量,shuffle的磁盤和內存,讀寫的數據量;如果是用的yarn模式來提交,課程最前面,從yarn的界面進去,點擊對應的application,進入Spark UI,查看詳情。

如果發現shuffle 磁盤的writeread,很大。這個時候,就意味着最好調節一些shuffle的參數。進行調優。首先當然是考慮開啓map端輸出文件合併機制。

調節上面說的那兩個參數。調節的時候的原則。spark.shuffle.file.buffer,每次擴大一倍,然後看看效果,64128;spark.shuffle.memoryFraction,每次提高0.1,看看效果。

 

不能調節的太大,太大了以後過猶不及,因爲內存資源是有限的,你這裏調節的太大了,其他環節的內存使用就會有問題了。

 

調節了以後,效果?map task內存緩衝變大了,減少spill到磁盤文件的次數;reduce端聚合內存變大了,減少spill到磁盤的次數,而且減少了後面聚合讀取磁盤文件的數量。

4.HashShuffleManager與SortShuffleManager

之前我們所講的,其實都是已經屬於Spark中,比較老舊的一種shuffle managerHashShuffleManager;這種manager,實際上,從spark 1.2.x版本以後,就不再是默認的選擇了。

HashShuffleManager的原理,以及對應的一些性能調優的點,基本上,之前幾講,咱們就都講過了。

spark 1.2.x版本以後,默認的shuffle manager,是什麼呢?SortShuffleManager

SortShuffleManagerHashShuffleManager兩點不同:

1SortShuffleManager會對每個reduce task要處理的數據,進行排序(默認的)。

2SortShuffleManager會避免像HashShuffleManager那樣,默認就去創建多份磁盤文件。一個task,只會寫入一個磁盤文件,不同reduce task的數據,用offset來劃分界定。之前講解的一些調優的點,比如consolidateFiles機制、map端緩衝、reduce端內存佔比。這些對任何shuffle manager都是有用的。

spark 1.5.x以後,對於shuffle manager又出來了一種新的managertungsten-sort(鎢絲),鎢絲sort shuffle manager。官網上一般說,鎢絲sort shuffle manager,效果跟sort shuffle manager是差不多的。

但是,唯一的不同之處在於,鎢絲manager,是使用了自己實現的一套內存管理機制,性能上有很大的提升, 而且可以避免shuffle過程中產生的大量的OOMGC,等等內存相關的異常。

5.reduce端緩衝大小調優

map端的task是不斷的輸出數據的,數據量可能是很大的。但是,其實reduce端的task,並不是等到maptask將屬於自己的那份數據全部寫入磁盤文件之後,再去拉取的。map端寫一點數據,reduce端task就會拉取一小部分數據,立即進行後面的聚合、算子函數的應用。每次reduece能夠拉取多少數據,就由buffer來決定。因爲拉取過來的數據,都是先放在buffer中的。然後才用後面的executor分配的堆內存佔比(0.2),hashmap,去進行後續的聚合、函數的執行。

5.1.調優

咱們假如說,你的Map端輸出的數據量也不是特別大,然後你的整個application的資源也特別充足。200executor5cpu core10G內存。其實可以嘗試去增加這個reduce端緩衝大小的,比如從48M,變成96M。那麼這樣的話,每次reduce task能夠拉取的數據量就很大。需要拉取的次數也就變少了。比如原先需要拉取100次,現在只要拉取50次就可以執行完了。對網絡傳輸性能開銷的減少,以及reduce端聚合操作執行的次數的減少,都是有幫助的。最終達到的效果,就應該是性能上的一定程度上的提升。

一定要注意,資源足夠的時候,再去做這個事兒。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章