隨着Spark的逐漸成熟完善, 愈來愈多的可配置參數被添加到Spark中來, 在Spark的官方文檔http://spark.apache.org/docs/latest/configuration.html 中提供了這些可配置參數中至關大一部分的說明.html
可是文檔的更新老是落後於代碼的開發的, 還有一些配置參數沒有來得及被添加到這個文檔中, 最重要的是在這個文檔中,對於許多的參數也只能簡單的介紹它所表明的內容的字面含義, 若是沒有必定的實踐基礎或者對其背後原理的理解, 每每沒法真正理解該如何針對具體應用場合進行合理配置。
算法
本文試圖經過闡述這其中部分參數的工做原理和配置思路, 和你們一塊兒探討一下如何根據實際場合對Spark進行配置優化。須要注意的是,理論上,沒有絕對正確的配置(不然也就不須要對應的配置參數了,Spark框架內部直接寫死就行了),因此請結合本身的實際狀況,辯證的看下面的內容。apache
因爲本文主要針對和性能相關的一些配置參數進行闡述,因此基本不會覆蓋其它和性能沒有太多關係的配置參數。網絡
因爲篇幅較長,因此在這裏分篇組織,若是要看最新完整的網頁版內容,能夠戳這裏:http://spark-config.readthedocs.org/,主要是便於更新內容數據結構
Shuffle 相關併發
Shuffle操做大概是對Spark性能影響最大的步驟之一(由於可能涉及到排序,磁盤IO,網絡IO等衆多CPU或IO密集的操做),這也是爲何在Spark 1.1的代碼中對整個Shuffle框架代碼進行了重構,將Shuffle相關讀寫操做抽象封裝到Pluggable的Shuffle Manager中,便於試驗和實現不一樣的Shuffle功能模塊。例如爲了解決Hash Based的Shuffle Manager在文件讀寫效率方面的問題而實現的Sort Base的Shuffle Manager。
框架
spark.shuffle.manager性能
用來配置所使用的Shuffle Manager,目前可選的Shuffle Manager包括默認的org.apache.spark.shuffle.sort.HashShuffleManager(配置參數值爲hash)和新的org.apache.spark.shuffle.sort.SortShuffleManager(配置參數值爲sort)。測試
這兩個ShuffleManager如何選擇呢,首先須要瞭解他們在實現方式上的區別。
優化
HashShuffleManager,故名思義也就是在Shuffle的過程當中寫數據時不作排序操做,只是將數據根據Hash的結果,將各個Reduce分區的數據寫到各自的磁盤文件中。帶來的問題就是若是Reduce分區的數量比較大的話,將會產生大量的磁盤文件。若是文件數量特別巨大,對文件讀寫的性能會帶來比較大的影響,此外因爲同時打開的文件句柄數量衆多,序列化,以及壓縮等操做須要分配的臨時內存空間也可能會迅速膨脹到沒法接受的地步,對內存的使用和GC帶來很大的壓力,在Executor內存比較小的狀況下尤其突出,例如Spark on Yarn模式。
SortShuffleManager,是1.1版本以後實現的一個試驗性(也就是一些功能和接口還在開發演變中)的ShuffleManager,它在寫入分區數據的時候,首先會根據實際狀況對數據採用不一樣的方式進行排序操做,底線是至少按照Reduce分區Partition進行排序,這樣來至於同一個Map任務Shuffle到不一樣的Reduce分區中去的全部數據均可以寫入到同一個外部磁盤文件中去,用簡單的Offset標誌不一樣Reduce分區的數據在這個文件中的偏移量。這樣一個Map任務就只須要生成一個shuffle文件,從而避免了上述HashShuffleManager可能遇到的文件數量巨大的問題
二者的性能比較,取決於內存,排序,文件操做等因素的綜合影響。
對於不須要進行排序的Shuffle操做來講,如repartition等,若是文件數量不是特別巨大,HashShuffleManager面臨的內存問題不大,而SortShuffleManager須要額外的根據Partition進行排序,顯然HashShuffleManager的效率會更高。
而對於原本就須要在Map端進行排序的Shuffle操做來講,如ReduceByKey等,使用HashShuffleManager雖然在寫數據時不排序,但在其它的步驟中仍然須要排序,而SortShuffleManager則能夠將寫數據和排序兩個工做合併在一塊兒執行,所以即便不考慮HashShuffleManager的內存使用問題,SortShuffleManager依舊可能更快。
spark.shuffle.sort.bypassMergeThreshold
這個參數僅適用於SortShuffleManager,如前所述,SortShuffleManager在處理不須要排序的Shuffle操做時,因爲排序帶來性能的降低。這個參數決定了在這種狀況下,當Reduce分區的數量小於多少的時候,在SortShuffleManager內部不使用Merge Sort的方式處理數據,而是與Hash Shuffle相似,直接將分區文件寫入單獨的文件,不一樣的是,在最後一步仍是會將這些文件合併成一個單獨的文件。這樣經過去除Sort步驟來加快處理速度,代價是須要併發打開多個文件,因此內存消耗量增長,本質上是相對HashShuffleMananger一個折衷方案。 這個參數的默認值是200個分區,若是內存GC問題嚴重,能夠下降這個值。
spark.shuffle.consolidateFiles
這個配置參數僅適用於HashShuffleMananger的實現,一樣是爲了解決生成過多文件的問題,採用的方式是在不一樣批次運行的Map任務之間重用Shuffle輸出文件,也就是說合並的是不一樣批次的Map任務的輸出數據,可是每一個Map任務所須要的文件仍是取決於Reduce分區的數量,所以,它並不減小同時打開的輸出文件的數量,所以對內存使用量的減小並無幫助。只是HashShuffleManager裏的一個折中的解決方案。
須要注意的是,這部分的代碼實現儘管原理上說很簡單,可是涉及到底層具體的文件系統的實現和限制等因素,例如在併發訪問等方面,須要處理的細節不少,所以一直存在着這樣那樣的bug或者問題,致使在例如EXT3上使用時,特定狀況下性能反而可能降低,所以從Spark 0.8的代碼開始,一直到Spark 1.1的代碼爲止也尚未被標誌爲Stable,不是默認採用的方式。此外由於並不減小同時打開的輸出文件的數量,所以對性能具體能帶來多大的改善也取決於具體的文件數量的狀況。因此即便你面臨着Shuffle文件數量巨大的問題,這個配置參數是否使用,在什麼版本中可使用,也最好仍是實際測試之後再決定。
spark.shuffle.spill
shuffle的過程當中,若是涉及到排序,聚合等操做,勢必會須要在內存中維護一些數據結構,進而佔用額外的內存。若是內存不夠用怎麼辦,那只有兩條路能夠走,一就是out of memory 出錯了,二就是將部分數據臨時寫到外部存儲設備中去,最後再合併到最終的Shuffle輸出文件中去。
這裏spark.shuffle.spill 決定是否Spill到外部存儲設備(默認打開),若是你的內存足夠使用,或者數據集足夠小,固然也就不須要Spill,畢竟Spill帶來了額外的磁盤操做。
spark.shuffle.memoryFraction/ spark.shuffle.safetyFraction
在啓用Spill的狀況下,spark.shuffle.memoryFraction(1.1後默認爲0.2)決定了當Shuffle過程當中使用的內存達到總內存多少比例的時候開始Spill。
經過spark.shuffle.memoryFraction能夠調整Spill的觸發條件,即Shuffle佔用內存的大小,進而調整Spill的頻率和GC的行爲。總的來講,若是Spill太過頻繁,能夠適當增長spark.shuffle.memoryFraction的大小,增長用於Shuffle的內存,減小Spill的次數。固然這樣一來爲了不內存溢出,對應的可能須要減小RDD cache佔用的內存,即減少spark.storage.memoryFraction的值,這樣RDD cache的容量減小,有可能帶來性能影響,所以須要綜合考慮。
因爲Shuffle數據的大小是估算出來的,一來爲了下降開銷,並非每增長一個數據項都完整的估算一次,二來估算也會有偏差,因此實際暫用的內存可能比估算值要大,這裏spark.shuffle.safetyFraction(默認爲0.8)用來做爲一個保險係數,下降實際Shuffle使用的內存閥值,增長必定的緩衝,下降實際內存佔用超過用戶配置值的機率。
spark.shuffle.spill.compress/ spark.shuffle.compress
這兩個配置參數都是用來設置Shuffle過程當中是否使用壓縮算法對Shuffle數據進行壓縮,前者針對Spill的中間數據,後者針對最終的shuffle輸出文件,默認都是True
理論上說,spark.shuffle.compress設置爲True一般都是合理的,由於若是使用千兆如下的網卡,網絡帶寬每每最容易成爲瓶頸。此外,目前的Spark任務調度實現中,以Shuffle劃分Stage,下一個Stage的任務是要等待上一個Stage的任務所有完成之後才能開始執行,因此shuffle數據的傳輸和CPU計算任務之間一般不會重疊,這樣Shuffle數據傳輸量的大小和所需的時間就直接影響到了整個任務的完成速度。可是壓縮也是要消耗大量的CPU資源的,因此打開壓縮選項會增長Map任務的執行時間,所以若是在CPU負載的影響遠大於磁盤和網絡帶寬的影響的場合下,也可能將spark.shuffle.compress 設置爲False纔是最佳的方案
對於spark.shuffle.spill.compress而言,狀況相似,可是spill數據不會被髮送到網絡中,僅僅是臨時寫入本地磁盤,並且在一個任務中同時須要執行壓縮和解壓縮兩個步驟,因此對CPU負載的影響會更大一些,而磁盤帶寬(若是標配12HDD的話)可能每每不會成爲Spark應用的主要問題,因此這個參數相對而言,或許更有機會須要設置爲False。
總之,Shuffle過程當中數據是否應該壓縮,取決於CPU/DISK/NETWORK的實際能力和負載,應該綜合考慮。
轉自:http://blog.csdn.net/colorant/article/details/38680581