spark shuffle的相關總結

什麼是shuffle?

發生 shuffle 操作主要是以下幾個算子:groupByKey、reduceByKey、countByKey、join,等等。

什麼時候需要shuffle writer?

前一個stage的ShuffleMapTask進行shuffle write,把數據存儲在blockManager上面,並且把數據位置元信息上報到driver的mapOutTrack組件中,下一個stage根據數據位置元信息,進行shuffle read ,拉取上個stage的輸出數據

spark2.1之前的shuffle方式

Hash Based Shuffle(該方式已於spark2.0廢棄) :

剛開始 每一個Mapper會根據Redude的數量創建出相應的bucket,bucket的數量是MR, 其中 M是Map的個數,R是Reduce的個數。這樣會產生大量的小文件,對文件系統壓力很大,而且不利於IO吞吐量。
後來做了一些優化,把在同一core上運行的多個Mapper輸出的合併到同一個文件,這樣文件數目就變成了cores R個了。這就是Consolidation機制。
Consolidation開啓方式: new SparkConf().set("spark.shuffle.consolidateFiles", "true")

Sort Based Shuffle

後面就引入了 Sort Based Shuffle , spark1.2以後默認使用該方式,map端的任務會按照Partition id以及key對記錄進行排序。同時將全部結果寫到一個數據文件中,同時生成一個索引文件。

Tungsten-Sort Based Shuffle

再後面就引入了Tungsten-Sort Based Shuffle,這個是直接使用堆外內存和新的內存管理模型,節省了內存空間和大量的gc,是爲了提升性能。

spark2.1的shuffle

spark 2.1中分爲三種writer , BypassMergeSortShuffleWriter, SortShuffleWriter 和 Unskai'qiafeShuffleWriter

BypassMergeSortShuffleWriter

實現細節:
BypassMergeSortShuffleWriter 和 Hash Shuffle的HashShuffleWriter實現基本一致,唯一的區別在於,map端的多個輸出文件會被彙總爲一個文件。所有分區的數據會合併爲同一個文件,會生成一個索引文件,是爲了索引到每個分區的起始地址,可以隨時access某個partiton的所有數據

需要注意的是,這種方式不宜有太多分區,因爲過程中會併發打開分區對應的臨時文件,會對文件系統造成相當大的壓力。

具體實現就是給每一個分區分配一個臨時文件,對每個record的key使用分區器(模式是hash,如果用戶自定義就使用自定義的分區器)找到對應分區的輸出文件句柄,直接寫入文件,沒有再內存中使用buffer.最後copyStream方法把所有的臨時分區文件拷貝到最終的輸出文件中,並且記錄每個分區的文件起始寫入位置,把這些位置數據寫入到索引文件中。

sortShuffleWriter:

SortShuffleWriter中的處理步驟就是:
使用PartitionedAppendOnlyMap 或者 PartitonedPairBuffer 在內存中進行排序,排序的鍵值對 K 是 (partitionId,hash(key))這樣的一個元組 如果超過內存limit,就會split到一個文件中,這個文件中元素也是有序的,首先是按照partitonId的排序,如果partitonId相同,再根據hash(key)進行比較排序如果需要輸出全局有序的文件的時候,就需要對之前所有的輸出文件 和 當前內存中的數據結構中的數據進行 merge sort ,進行全局排序

SortShuffleWriter使用ExternalSorter來對內存中的數據進行排序,ExternalSorter內部維護了兩個集合PartitionedAppendOnlyMay,PartitonedPairBuffer,兩者都是使用了hash table數據結構,如果需要進行aggregation,就使用PartitonedAppendOnlyMap(支持lookup 某個key,如果之前存儲過相同key的K-V元素,就需要進行aggregation,然後再存入aggregation後的K-V),否則使用PartitonedPairBuffer(只進行添加K-V元素)

在aggregation的時候,就是使用定義的func進行聚合,比如你的算子是reduceByKey(+),這個func就是加法運算,如果兩個key相同,就會先找到所有相同的key進行reduce(+)操作,算出一個總結果result,然後輸出數據(K,Result)元素

unsafeShuffleWriter:

Serializer支持relocation:原始數據首先被序列化處理,並且再也不需要反序列,在其對應的元數據被排序後,需要Serializer支持relocation,在指定位置讀取對應數據

UnsafeShuffleWriter裏面維護了一個外部排序, 每次根據partitionId進行排序,如果內存滿了直接split磁盤上,這個和sortShuffleWriter有什麼區別呢,這裏只根據record的partition id先在內存ShuffleInMemorySorter中進行排序,排好序的數據經過序列化壓縮輸出到一個臨時文件中的一段,並記錄每個分區段的seek位置,方便後續可以單獨讀取每個分區的數據,讀取流經過解壓反序列化,就可以正常讀取了

unsageShuffleWriter限制條件:

unsageShuffleWriter需要Serializer支持relocation,並且不支持key排序

使用哪種writer的判斷依據:
a.是否開啓了mapSideCombine 並且分區數目是否小於spark.shuffle.sort.bypassMergeThreshold(默認值爲200),如果是的話 執行BypassMergeSortShuffleWriter:
b.如果不滿足條件a ,並且serializer支持relocation ,沒有聚合操作,以及分區數目小於16777215,則使用unsageShuffleWriter,否則使用SortShuffleWriter

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