Spark Shuffle 分析

1.Shuffle 原理

1.1 概述

1.1.1 Map task端操作

1.1.2 Reduce task 端操作

1.1.3 Spark Shuffle

2.Spark Shuffle 的實現

2.1 Shuffle 的寫操作

2.1.1 基於 Hash 的 Shuffle 寫操作

2.1.2 基於 sort 的 Shuffle 寫操作

2.1.3  Shuffle 讀操作


1.Shuffle 原理

1.1 概述

Shuffle 描述着數據從map task輸出到reduce task輸入的這段過程。在分佈式情況下,reduce task需要跨節點去拉取
其它節點上的map task結果。這一過程將會產生網絡資源消耗和內存,磁盤IO的消耗。

Shuffle 是連接 Map 和 Reduce 之間的橋樑,Map 的輸出要用到 Reduce 中必須經過 Shuffle 這個環節。由於Shuffle 階段涉及磁盤的讀寫和網絡傳輸,因此 Shuffle 的性能高低直接影響整個程序的性能和吞吐量。下圖Hadoop的MapReduce 整個流程,其中Shuffle 階段是介幹Map 階段和 Reduce 階段之間。

1.1.1 Map task端操作

每個 map task 都有一個內存緩衝區(默認是100MB),存儲着 map 的輸出結果,當緩衝區快滿的時候需要將緩衝區的數據以一個臨時文件的方式存放到磁盤,當整個map task 結束後再對磁盤中這個map task 產生的所有臨時文件做合併,生成最終的正式輸出文件,然後等待reduce task來拉數據。

Spill過程:這個從內存往磁盤寫數據的過程被稱爲Spill,中文可譯爲溢寫。整個緩衝區有個溢寫的比例spill.percent(默認是0.8),當達到閥值時map task 可以繼續往剩餘的memory寫,同時溢寫線程鎖定已用memory,先對key(序列化的字節)做排序,如果client程序設置了Combiner,那麼在溢寫的過程中就會進行局部聚合。

Merge過程:每次溢寫都會生成一個臨時文件,在map task真正完成時會將這些文件歸併成一個文件,這個過程叫做Merge。

1.1.2 Reduce task 端操作

當某臺 TaskTracker上 的所有 map task 執行完成,對應節點的 reduce task 開始啓動,簡單地說,此階段就是不斷地拉取
(Fetcher)每個 map task 所在節點的最終結果,然後不斷地做merge形成reduce task的輸入文件。

Copy過程:Reduce進程啓動一些數據copy線程(Fetcher)通過HTTP協議拉取TaskTracker的map階段輸出文件

Merge過程:Copy過來的數據會先放入內存緩衝區(基於JVM的heap size設置),如果內存緩衝區不足也會發生map task的
spill(sort 默認,combine 可選),多個溢寫文件時會發生map task的merge

1.1.3 Spark Shuffle

Spark 作爲 MapReduce 框架的一種實現,自然也實現了Shuffle 的邏輯。

Spark Shuffle:Shuffle 在中文的意思是“洗牌、混洗”的意思,在 MapReduce 過程中需要各個節點上的同一個類型數據彙集到某一節點中進行計算,把這些分佈在不同節點的數據按照一定的規則彙集到一起的過程稱爲 Shuffle

  • 數據量非常大,達到TB甚至PB級別。這些數據分散到數百臺甚至數千臺的集羣中運行,如果管理爲後續的任務創建數據衆多的文件,以及處理大小超過內存的數據量呢?
  • 如果對結果進行序列化和反序列化,以及傳輸之前如何進行壓縮呢?

2.Spark Shuffle 的實現

Spark中有好幾個Shuffle的實現。那具體使用哪個Shuffle的實現,取決於 spark.shuffle.manager 這個參數。可能的選項是  hash, sort, tungsten-sort。從Spark 1.2.0開始,sort 是默認選項。

2.1 Shuffle 的寫操作

2.1.1 基於 Hash 的 Shuffle 寫操作

在 Hadoop 中 Reduce 所處理的數據都是經過排序的,但在實際處理中很多場景並不需要排序,因此在 Spark1.0之間實現的是基於哈希的Shuffle寫操作機制。在該機制中每一個 Mapper 會根據 Reduce 的數量創建出相應的 bucket,bucket 的數據是 M*R,其中M是Map的個數,R是Reduce的個數;Mapper 生成的結果會根據設置的Partition算法填充到每個bucket中。這裏的bucket是一個抽象的概念,在該機制中每個bucket對應一個文件;當Reduce啓動時,會根據任務的編號和所依賴的Mapper的編號從遠端或者是本地取得相應的bucket作爲Reduce任務的輸入進行處理

相對於傳統的MapReduce,Spark假定大多數情況下Shufle的數據排序是不必要的,比如Word Count,強制進行排序只能使性能變差,因此Spark並不在Reduce 端做merge sort,而是使用聚合(aggregator)。聚合實際上是一個HashMap,它以當前任務輸出結果的Key爲鍵,以任意要combine類型爲值,當在Word Count的Reduce進行單詞計數時,它會將Shuffle讀到的每一個鍵值對更新或者插入到HashMap中。這樣就不需要預先把所有的鍵值對進行merge sort,而是來一個處理一個,省下了外部排序這個步驟。

Hash Shuffle的優點:

  • 非常快速。不需要排序,也不需要維護哈希表。沒有給數據排序時的內存的開銷。
  • 沒有額外的IO開銷。數據只被讀寫一次。

Hash Shuffle的缺點:

  • 當Reducer的數量很多時,很容易有性能問題
  • 當大量文件被寫到文件系統時,會產生大量的Random IO。而Random IO是最慢的,比Sequential IO要慢100倍。

2.1.2 基於 sort 的 Shuffle 寫操作

爲了緩解 Shuffe 過程中產生文件過多的文件和 Writer Handlerde 緩存開銷過大的問題,在 Spark1.1 版本中借鑑 Hadoop Shuffle中的處理方式,引入基於排序的Shufle寫操作機制。在該機制中,每個 Shuffle Map Task 不會爲後續的每個任務創建單獨的件,而是會將所有的結果寫到同一個文件中,對應的生成一個 Index 文件進行索引。通過這種機制避免了大量文件的產生,一方面可以減輕文件系統管理衆多文件的壓力,另一方面可以減少Writer Handlerde緩存所佔用的內存大小,節省了內存同時避免了GC的風險和頻率。

基於排序的 Shuffle 寫操作解決了文件創建數目過多的問題,也化解了在需要排序時內存佔用過大的問題。

2.1.3  Shuffle 讀操作


(1)在SparkEnv啓動時,會對ShuffleManager、BlockManager 和MapOutputTracker 等實例化。ShufleManager配置項有HashShuffleManager、SortShuffleManager和自定義的ShuffleManager 等3種選項,前兩種在Shuffle 讀中均實例化的BlockStoreShuffleReader,但是在HashShuffleManager中所持有的是FileShuffleBlockResolver實例,SortShuffleManager中所持有的IndexShufleBlockResolver實例。對於第一個問題在該步驟就能夠知道答案:將根據不同的寫入方式將採取相同的讀取方式,讀取的數據放在哈希列表中便於後續處理。
(2)在BlockStoreShufleReader 的read方法中,調用MapOutputTracker的getMapSizesByExecutorld方法,由Executor的MapOutputTracker發送獲取結果狀態的GetMapOutputStatuses消息給Driver 端的MapOutputTrackerMaster,請求獲取上Shuffle輸出結果對應的MapStatus,在該MapStatus存放了結果數據的位置信息,ShufmleMapTask 執行結果元信息。通過請求Driver 端的MapOutputTrackerMaster,可以得到上游Shuffle結果的位置信息。
(3)知道Shuffle結果的位置信息後,對這些位置進行篩選,判斷當前運行的數據是從本地還是從遠程節點獲取。如果是本地獲取,直接調用BlockManager的getBlockData方法,在讀取數據的時候會根據寫入方式採取不同ShuffmeBlockResolver 讀取:如果是在遠程節點上,需要通過Netty網絡方式讀取數據。在遠程讀取的過程中使用多線程的方式進行讀取,一般來說,會啓動5個線程到5個節點進行讀取所有,每次請求的數據大小不會超過系統設置的1/5,該大小由spark.reducer.maxSizelnFlight配置項進行設置,默認情況該配置爲48MB。
(4)讀取數據後,判斷ShuffleDependency是否定義聚合(Aggregation),如果需要,則根據鍵值進行聚合。需要注意的是,如果在上游ShufftleMapTask已經做了合併,則在合併數據的基礎上做鍵值聚合。待數據處理完畢後,使用外部排序(ExternalSorter)對數據進行排序並放入存儲中,至此完成Shufle讀數據操作。

 

參考 《圖解spark 核心技術與實踐》《Spark內核設計的藝術》 https://www.jianshu.com/p/a3bb3001abae

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