Spark調優

Spark調優

數據序列化

  1. Java serialization:Spark默認使用Java的ObjectOutputStream框架來序列化對象,可以對任何實現了
    java.io.Serializable的任何類進行序列化。用戶也可以通過繼承來實現更緊密的序列化性能控制。
  2. Kryo serialization:Spark也可以使用Kryo庫(version 2)來實現更快的對象序列化。Kryo比Java序列化更快、數據格式更緊湊,但不支持所有的Serializable類型。用戶如果希望使用Kryo來獲取更好的性能,需要先去
    註冊應用程序中會使用到的類。
val conf = new SparkConf().setMaster(...).setAppName(...)
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)
如果用戶的對象很大,也需要去增加spark.kryoserializer.buffer配置項。

內存調優

1、Spark內存使用情況主要有兩類:執行和緩存,並且執行和緩存共享一個區域
2、當不使用執行內存時,存儲可以獲得所有內存,反之亦然
3、不適用緩存的應用程序可以將整個空間空域執行,從而避免了不必要的磁盤溢出
4、應用程序也可以保留最小的存儲空間,以免數據塊被驅逐

spark.memory.fraction表示執行和緩存使用的公共區域大小,
默認爲JVM 堆內存的0.6JVM剩餘的0.4是爲用戶數據結構,
Spark內部metastore預留spark.memory.storageFraction 
表示提供給緩存數據塊避免受到執行驅逐的存儲空間佔比,
默認0.5

在這裏插入圖片描述

Spark Memory:系統框架運行時需要使用的空間
User Memeory:RDD中用戶自定義的類等
Reserved Memory:防止OOM的預留空間

數據結構調優

1、將數據結構設計爲更傾向於數組結構和基本類型,而不是標準的Java或是Scala集合類(例如. HashMap)。
2、儘可能避免包含需要小對象和指針的嵌套結構
3、考慮使用數字ID或是枚舉對象而不是字符串key
4、如果你的RAM小於32GB,設置JVM參數 -XX:+UseCompressedOops 來讓指針變爲4個字節而不是8個字節。

GC調優

1、Java對內存被分爲兩個區域,新生代和老年代。新生代是爲了保存壽命較短的對象,而老年代是爲了保持壽命更長的對象。
2、新生代被進一步劃分爲三個區域: Eden,Survivor1,Survivor2
垃圾收集過程的簡化描述:當Eden區使用佔滿時,一個minor GC會在Eden中發生,仍然存活的對象會從Eden和Survivor1區域中複製到Survivor2。如果一個對象存活的時間夠久或是Survivor2區域空間佔滿時,它會移動到老年代。最後當老年空間接近佔滿時,會觸發full GC。

3、過收集GC狀態來檢查是否有太多GC。
4、如果有許多minor GC但是沒有太多major GC,可以爲Eden分配更多內存。

Shuffle調優

spark.shuffle.file.buffer
默認值:32k
參數說明:該參數用於設置shuffle write task的BufferedOutputStream的buffer緩衝大小。將數據寫到磁盤文件之前,會先寫入buffer緩衝中,待緩衝寫滿之後,纔會溢寫到磁盤。
調優建議:如果作業可用的內存資源較爲充足的話,可以適當增加這個參數的大小(比如64k),從而減少shuffle write過程中溢寫磁盤文件的次數,也就可以減少磁盤IO次數,進而提升性能。在實踐中發現,合理調節該參數,性能會有1%~5%的提升。


spark.reducer.maxSizeInFlight
默認值:48m
參數說明:該參數用於設置shuffle read task的buffer緩衝大小,而這個buffer緩衝決定了每次能夠拉取多少數據。
調優建議:如果作業可用的內存資源較爲充足的話,可以適當增加這個參數的大小(比如96m),從而減少拉取數據的次數,也就可以減少網絡傳輸的次數,進而提升性能。在實踐中發現,合理調節該參數,性能會有1%~5%的提升。


spark.shuffle.io.maxRetries
默認值:3
參數說明:shuffle read task從shuffle write task所在節點拉取屬於自己的數據時,如果因爲網絡異常導致拉取失敗,是會自動進行重試的。該參數就代表了可以重試的最大次數。如果在指定次數之內拉取還是沒有成功,就可能會導致作業執行失敗。
調優建議:對於那些包含了特別耗時的shuffle操作的作業,建議增加重試最大次數(比如60次),以避免由於JVM的full gc或者網絡不穩定等因素導致的數據拉取失敗。在實踐中發現,對於針對超大數據量(數十億~上百億)的shuffle過程,調節該參數可以大幅度提升穩定性。


spark.shuffle.io.retryWait
默認值:5s
參數說明:具體解釋同上,該參數代表了每次重試拉取數據的等待間隔,默認是5s。
調優建議:建議加大間隔時長(比如60s),以增加shuffle操作的穩定性。


spark.shuffle.memoryFraction
默認值:0.2
參數說明:該參數代表了Executor內存中,分配給shuffle read task進行聚合操作的內存比例,默認是20%。
調優建議:在資源參數調優中講解過這個參數。如果內存充足,而且很少使用持久化操作,建議調高這個比例,給shuffle read的聚合操作更多內存,以避免由於內存不足導致聚合過程中頻繁讀寫磁盤。在實踐中發現,合理調節該參數可以將性能提升10%左右。


spark.shuffle.manager
默認值:sort
參數說明:該參數用於設置ShuffleManager的類型。Spark 1.5以後,有三個可選項:hash、sort和tungsten-sort。HashShuffleManager是Spark 1.2以前的默認選項,但是Spark 1.2以及之後的版本默認都是SortShuffleManager了。tungsten-sort與sort類似,但是使用了tungsten計劃中的堆外內存管理機制,內存使用效率更高。
調優建議:由於SortShuffleManager默認會對數據進行排序,因此如果你的業務邏輯中需要該排序機制的話,則使用默認的SortShuffleManager就可以;而如果你的業務邏輯不需要對數據進行排序,那麼建議參考後面的幾個參數調優,通過bypass機制或優化的HashShuffleManager來避免排序操作,同時提供較好的磁盤讀寫性能。這裏要注意的是,tungsten-sort要慎用,因爲之前發現了一些相應的bug。


spark.shuffle.sort.bypassMergeThreshold
默認值:200
參數說明:當ShuffleManager爲SortShuffleManager時,如果shuffle read task的數量小於這個閾值(默認是200),則shuffle write過程中不會進行排序操作,而是直接按照未經優化的HashShuffleManager的方式去寫數據,但是最後會將每個task產生的所有臨時磁盤文件都合併成一個文件,並會創建單獨的索引文件。
調優建議:當你使用SortShuffleManager時,如果的確不需要排序操作,那麼建議將這個參數調大一些,大於shuffle read task的數量。那麼此時就會自動啓用bypass機制,map-side就不會進行排序了,減少了排序的性能開銷。但是這種方式下,依然會產生大量的磁盤文件,因此shuffle write性能有待提高。


spark.shuffle.consolidateFiles
默認值:false
參數說明:如果使用HashShuffleManager,該參數有效。如果設置爲true,那麼就會開啓consolidate機制,會大幅度合併shuffle write的輸出文件,對於shuffle read task數量特別多的情況下,這種方法可以極大地減少磁盤IO開銷,提升性能。


調優建議:如果的確不需要SortShuffleManager的排序機制,那麼除了使用bypass機制,還可以嘗試將spark.shffle.manager參數手動指定爲hash,使用HashShuffleManager,同時開啓consolidate機制。在實踐中嘗試過,發現其性能比開啓了bypass機制的SortShuffleManager要高出10%~30%。

其他調優

1、並行級別
推薦爲集羣中的每個CPU分配2-3個任務
2、reduce內存使用
Spark的shuffle操作(sortByKey,groupByKey,reduceByKey,join等等)會在每個任務中創建一個hash
table來執行grouping操作,這個操作經常會很大。
處理方案是增加並行度,讓每個任務獲取到的數據集更小。
3、廣播大變量
如果你的任務用到了driver中的一個大的對象(例如一個static lookup table),可以考慮將它變爲廣播變量。
4、數據本地行
移動計算不移動數據
5、兩階段聚合,先將數據添加隨機數,reduce,再將隨機出去除,reduce

Spark Shuffle

1、未經優化的HashShuffleManager

1.1、shuffle write階段

  1. 在Stage結束之後,爲下一個Stage要處理的數據按照Key進行“分類”,對相同的key執行hash算法,從而將相同的key寫入到同一個磁盤文件中,而每一個磁盤文件都只屬於下游stage的一個task,數據會先寫道內存緩存中,內存緩存填滿之後,寫道文件
  2. 下游的stage存在多少task,上游的每一個task就要產生這些個文件

1.2、shuffle read階段

  1. 此時該stage的每一個task就需要將上一個stage的計算結果中的所有相同key,從各個節點上通過網絡都拉取到自己所在的節點上,然後進行key的聚合或連接等操作。
  2. 由於shuffle write的過程中,task給下游stage的每個task都創建了一個磁盤文件,因此shuffle read的過程中,每個task只要從上游stage的所有task所在節點上,拉取屬於自己的那一個磁盤文件即可。

2、優化後的HashShuffleManager

  1. 跟未經優化的Hash ShuffleManager相比,多了一個shuffleFileGroup機制,往下游寫的時候,shuffleFileGroup可以複用,第二,三…n批task可以寫入第一批的磁盤文件中

3、SortShuffleManager

3.1 普通運行機制

  1. 採樣,根據不同的shuffle算子,選用不同的數據結構,聚合類的算子,使用map,一邊map一邊聚合
  2. 普通shuffle算子,Array數據結構
  3. 在溢寫到磁盤文件之前,會先根據key對內存數據結構中已有的數據進行排序,排序過後,會
    分批將數據寫入磁盤文件,默認的batch數量是10000條。
  4. 一個task將所有數據寫入內存數據結構的過程中,會發生多次磁盤溢寫操作,也就會產生多個臨時文件。最後會將之前所有的臨時磁盤文件都進行合併,這就是merge過程,此時會將之前所有臨時磁盤文件中的數據讀取出來,然後依次寫入最終的磁盤文件之中。此外,由於一個task就只對應一個磁盤文件,也就意味着該task爲下游stage的task準備的數據都在這一個文件中,因此還會單獨寫一份索引文件,其中標識了下游各個task的數據在文件中的start offset與end offset。

3.2 ByPass機制

  1. 當Task數量沒有超過spark.shuffle.sort.bypassMergeThreshold參數的值。
    使用未經優化的HashShuffleManager,但是最後會合並文件,並建立索引文件
  2. 並且在shuffle過成功,不需要進行排序操作
發佈了143 篇原創文章 · 獲贊 40 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章