標籤:spark sort-based shuffle內幕徹底解密
內容:
1、為什麼使用Sorted-Based Shuffle;
2、Sorted-Based Shuffle實戰;
3、Sorted-Based Shuffle內幕;
4、Sorted-Based Shuffle的不足;
最常用的Shuffle方式,Sorted-Based Shuffle涉及了大規模Spark開發、營運時核心問題,以及答案的要害所在。
必須掌握這一講內容。
本課是從Spark初級人才成功升級為Spark中級人才的通道。
稍有水平的大公司,面試內容本講肯定會涉及。
==========為什麼需要Sorted-Based Shuffle ============
1、Spark開始是Hash-Based Shuffle,Shuffle一般包含兩階段的任務:產生Shuffle資料的階段(Map階段,額外補充,需要實現ShuffleManager中的getWriter來寫資料(資料可以 通過BlockManager寫在Memry、Disk、Tachyon等,例如想非常快的Shuffle,此時考慮可以把資料寫在記憶體中,但是記憶體不穩定,建議採用MemoeyAndDisk方式,更保險可以MemoeyAndDisk 2方式,但是開銷也大)、使用Shuffle資料的階段(Reduce階段,額外補充,需要實現ShuffleManager的getReader,Reader會向Driver去擷取上一個Stage產生的Shuffle資料)
2、Spark是鏈式運算式,可以在一個鏈條上有很多Mapper和很多Reducer。
如果不止一個Stage,Spark除了最後的Stage是Reducer之外,開始Stage是整個Job的Mapper,中間的每個Stage即是Mapper又是Reducer,它是上一個Stage的Reducer,下一個Stage的Mapper。
如果只有一個Stage,則這個Job就相當於只有一個Mapper階段,不會產生Shuffle,適合於簡單的ETL。
3、Spark Shuffle在最開始的時候,只支援Hash-Based Shuffle,而Hash-Based Shuffle預設Mapper階段會為Reducer階段的每一個Task,單獨建立一個檔案來儲存該task中要使用的資料,但是在一些情況下(例如資料量非常大的情況)會造成大量檔案(M*R,M代表Mapper中的所有的並行任務數量,R代表Reducer中所有的並行任務數量)的隨機磁碟IO操作,且會形成大量的memory消耗,極易造成OOM,這是致命的問題,第一不能處理大規模的資料,第二Spark不能運行在大規模的分布式叢集上!後來的方案是加入了Shuffle Consolidate機制來將Shuffle時候產生的檔案數量減少到C*R個(C代表在Mapper端同時能夠使用的cores的數量,R代表Reducer中所有的並行任務數量),但是此時如果Reducer端的並行資料分區(或者任務)過多的話,則C*R可能依舊過大,此時依舊沒有逃脫檔案開啟過多的厄運;
Spark(Spark 1.1版本之前)在引入Sort-Based Shuffle之前,比較適用於中小規模的大資料處理。
4、為了讓Spark在更大規模的叢集上更高效能的處理更大規模的資料,於是就引入了Sort-Based Shuffle(Spark1 1.1版本開始),Spark可以勝任任意規模(包含PB層級及PB以上的層級)大資料的處理,尤其是隨著鎢絲計劃的引入和最佳化,把Spark更快速的在更大規模的叢集處理更海量的資料的能力推向了一個巔峰!
想到了Hadoop的Map Reduce Shuffle,它是排序的。有環形記憶體緩衝區,既有資料由有索引。
5、Spark 1.6版本支援至少三種類型的Shuffle
// Let the user specify short names for shuffle managers
val shortShuffleMgrNames = Map(
"hash" -> "org.apache.spark.shuffle.hash.HashShuffleManager",
"sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager",
"tungsten-sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager")
val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
val shuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase, shuffleMgrName)
val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)
實現Shuffle Manager介面可以根據自己的業務實際需要去最佳化的使用自訂的Shuffle實現;
6、Spark1.6預設採用的是Sort-Based Shuffle的方式,見第5點Spark的原始碼,也同時說明Spark-defalut
conf檔案中可以配置spark.shuffle.manager來設定Shuffle Manager的實現
Sort-Based Shuffle不會為每個Reducer中的Task產生一個單獨的檔案,相反,Sort-Based Shuffle 會把Mapper中每個ShuffleMapTask所有的輸出資料唯寫入一個檔案中,因為每個ShuffleMapTask中的資料會被分類,所以Sort-Based Shuffle 使用了index檔案儲存體具體ShuffleMapTask輸出資料在同一個Data中是如何進行分類的資訊(資料要分類,才要Shuffle,Shuffle之後還要聚類)。所以說Sort-Based Shuffle會在Mapper中的每一個Shuffle Map Task中產生兩個檔案,其中Data是儲存Shuffle當前Task的Shuffle輸出的,Index檔案中儲存資料了Data檔案中的資料通過Partitioner的分類資訊。此時下一個階段的Stage中的Task就是根據這個Index檔案擷取自己所要抓取的上一個Stage中的Shuffle Map Task產生的資料的。
Sort-Based Shuffle產生的臨時檔案的數量正確的答案是2M(M代表Mapper中並行partition的總數量,其實就是Mapper端Task的總數量,這和實際並行的數量是兩碼事);
回顧整個Shuffle的曆史,Shuffle產生的臨時檔案的數量的變化為依次為:
Basic Hash Shuffle M*R;
Consalisate方式Hash Shuffle C*R;
Sort-Based Shuffle 2M;
==========在叢集中動手實戰Sort-Based Shuffle============
自己先建個目錄,上傳幾個檔案
[email protected]:/usr/local/hadoop-2.6.0/sbin# hadoop dfs -mkdir /library
DEPRECATED: Use of this script to execute hdfs command is deprecated.
Instead use the hdfs command for it.
16/02/13 13:18:41 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[email protected]:/usr/local/hadoop-2.6.0/sbin# hadoop dfs -mkdir /library/dataforSortedShuffle
DEPRECATED: Use of this script to execute hdfs command is deprecated.
Instead use the hdfs command for it.
16/02/13 13:19:27 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[email protected]:/usr/local/hadoop-2.6.0# hadoop dfs -put LICENSE.txt /library/dataforSortedShuffle/
DEPRECATED: Use of this script to execute hdfs command is deprecated.
Instead use the hdfs command for it.
16/02/13 13:21:06 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
./spark-shell --master spark://Master:7077,Worker1:7077,Worker2:7077
sc.textFile("/library/dataforSortedShuffle").flatMap(_.split(" ")).map(word=>(word,1)).reduceByKey(_+_).saveAsTextFile("/library/dataoutput1")
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
並行度6
臨時資料檔案和索引檔案,2個資料2個索引:
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
叢集的三台機器全部有2個索引2個資料,一共是6個資料6個索引的臨時檔案,所以就如上面理論所說的,說明預設的是sort的方式產生的 ,並行度為6的時候,會產生6*2個臨時檔案,2M個檔案!!!
命名規則
shuffle_0_0_0.data
shuffle_0_1_0.data
shuffle_0_2_0.data
等等
shuffle_0_0_0.index
shuffle_0_1_0.index
shuffle_0_2_0.index
等等
過一會,臨時檔案就被刪除了。。
在Sort-Based Shuffle中,Reducer是怎麼樣擷取自己需要的資料的呢?
具體而言,Reducer首先找Drvier去擷取父Stage中每個Shuffle Map Task輸出的位置資訊,根據位置資訊擷取index檔案,解析index檔案,從解析的index檔案中擷取index檔案中擷取Data檔案中屬於自己的那部分內容。
預設Sort-Based Shuffle的幾個缺陷:
1、如果Mapper中Task的數量過大,依舊會產生很多小檔案;此時在Shuffle傳遞資料的過程中到Reducer端,Reduce會需要同時開啟大量的記錄來進行還原序列化,導致大量的記憶體消耗,和gc巨大負擔,造成系統緩慢甚至崩潰;
2、如果需要在分區內也進行排序的話,此時需要Mapper端和Reducer端的兩次排序
王家林老師名片:
中國Spark第一人
新浪微博:http://weibo.com/ilovepains
公眾號:DT_Spark
部落格:http://blog.sina.com.cn/ilovepains
手機:18610086859
QQ:1740415547
郵箱:[email protected]
本文出自 “一枝花傲寒” 部落格,謝絕轉載!
Spark Sort-Based Shuffle內幕徹底解密(DT大資料夢工廠)