Spark 性能相關參數配置詳解-任務調度篇

隨着Spark的逐漸成熟完善越來越多的可配置參數被添加到Spark中來本文試圖通過闡述這其中部分參數的工作原理和配置思路和大家一起探討一下如何根據實際場合對Spark進行配置優化。

 

由於篇幅較長,所以在這裏分篇組織,如果要看最新完整的網頁版內容,可以戳這裏:http://spark-config.readthedocs.org/,主要是便於更新內容

 

 

schedule調度相關

 

調度相關的參數設置,大多數內容都很直白,其實無須過多的額外解釋,不過基於這些參數的常用性(大概會是你針對自己的集羣第一步就會配置的參數),這裏多少就其內部機制做一些解釋。

 

spark.cores.max

 

一個集羣最重要的參數之一,當然就是CPU計算資源的數量。spark.cores.max 這個參數決定了在StandaloneMesos模式下,一個Spark應用程序所能申請的CPU Core的數量。如果你沒有併發跑多個Spark應用程序的需求,那麼可以不需要設置這個參數,默認會使用spark.deploy.defaultCores的值(而spark.deploy.defaultCores的值默認爲Int.Max,也就是不限制的意思)從而應用程序可以使用所有當前可以獲得的CPU資源。

 

針對這個參數需要注意的是,這個參數對Yarn模式不起作用,YARN模式下,資源由Yarn統一調度管理,一個應用啓動時所申請的CPU資源的數量由另外兩個直接配置Executor的數量和每個Executorcore數量的參數決定。(歷史原因造成,不同運行模式下的一些啓動參數個人認爲還有待進一步整合)

 

此外,在Standalone模式等後臺分配CPU資源時,目前的實現中,在spark.cores.max允許的範圍內,基本上是優先從每個Worker中申請所能得到的最大數量的CPU core給每個Executor,因此如果人工限制了所申請的Max Core的數量小於StandaloneMesos模式所管理的CPU數量,可能發生應用只運行在集羣中部分節點上的情況(因爲部分節點所能提供的最大CPU資源數量已經滿足應用的要求),而不是平均分佈在集羣中。通常這不會是太大的問題,但是如果涉及數據本地性的場合,有可能就會帶來一定的必須進行遠程數據讀取的情況發生。理論上,這個問題可以通過兩種途徑解決:一是StandaloneMesos的資源管理模塊自動根據節點資源情況,均勻分配和啓動Executor,二是和Yarn模式一樣,允許用戶指定和限制每個ExecutorCore的數量。 社區中有一個PR試圖走第二種途徑來解決類似的問題,不過截至我寫下這篇文檔爲止(2014.8),還沒有被Merge

 

spark.task.cpus

 

這個參數在字面上的意思就是分配給每個任務的CPU的數量,默認爲1。實際上,這個參數並不能真的控制每個任務實際運行時所使用的CPU的數量,比如你可以通過在任務內部創建額外的工作線程來使用更多的CPU(至少目前爲止,將來任務的執行環境是否能通過LXC等技術來控制還不好說)。它所發揮的作用,只是在作業調度時,每分配出一個任務時,對已使用的CPU資源進行計數。也就是說只是理論上用來統計資源的使用情況,便於安排調度。因此,如果你期望通過修改這個參數來加快任務的運行,那還是趕緊換個思路吧。這個參數的意義,個人覺得還是在你真的在任務內部自己通過任何手段,佔用了更多的CPU資源時,讓調度行爲更加準確的一個輔助手段。

 

 

spark.scheduler.mode

 

這個參數決定了單個Spark應用內部調度的時候使用FIFO模式還是Fair模式。是的,你沒有看錯,這個參數只管理一個Spark應用內部的多個沒有依賴關係的Job作業的調度策略。

 

如果你需要的是多個Spark應用之間的調度策略,那麼在Standalone模式下,這取決於每個應用所申請和獲得的CPU資源的數量(暫時沒有獲得資源的應用就Pending在那裏了),基本上就是FIFO形式的,誰先申請和獲得資源,誰就佔用資源直到完成。而在Yarn模式下,則多個Spark應用間的調度策略由Yarn自己的策略配置文件所決定。

 

那麼這個內部的調度邏輯有什麼用呢?如果你的Spark應用是通過服務的形式,爲多個用戶提交作業的話,那麼可以通過配置Fair模式相關參數來調整不同用戶作業的調度和資源分配優先級。

 

 

spark.locality.wait

 

spark.locality.wait和spark.locality.wait.process,spark.locality.wait.node, spark.locality.wait.rack這幾個參數影響了任務分配時的本地性策略的相關細節。

 

Spark中任務的處理需要考慮所涉及的數據的本地性的場合,基本就兩種,一是數據的來源是HadoopRDD; 二是RDD的數據來源來自於RDD Cache(即由CacheManagerBlockManager中讀取,或者Streaming數據源RDD)。其它情況下,如果不涉及shuffle操作的RDD,不構成劃分StageTask的基準,不存在判斷Locality本地性的問題,而如果是ShuffleRDD,其本地性始終爲No Prefer,因此其實也無所謂Locality

 

在理想的情況下,任務當然是分配在可以從本地讀取數據的節點上時(同一個JVM內部或同一臺物理機器內部)的運行時性能最佳。但是每個任務的執行速度無法準確估計,所以很難在事先獲得全局最優的執行策略,當Spark應用得到一個計算資源的時候,如果沒有可以滿足最佳本地性需求的任務可以運行時,是退而求其次,運行一個本地性條件稍差一點的任務呢,還是繼續等待下一個可用的計算資源已期望它能更好的匹配任務的本地性呢?

 

這幾個參數一起決定了Spark任務調度在得到分配任務時,選擇暫時不分配任務,而是等待獲得滿足進程內部/節點內部/機架內部這樣的不同層次的本地性資源的最長等待時間。默認都是3000毫秒。

 

基本上,如果你的任務數量較大和單個任務運行時間比較長的情況下,單個任務是否在數據本地運行,代價區別可能比較顯著,如果數據本地性不理想,那麼調大這些參數對於性能優化可能會有一定的好處。反之如果等待的代價超過帶來的收益,那就不要考慮了。

 

特別值得注意的是:在處理應用剛啓動後提交的第一批任務時,由於當作業調度模塊開始工作時,處理任務的Executors可能還沒有完全註冊完畢,因此一部分的任務會被放置到No Prefer的隊列中,這部分任務的優先級僅次於數據本地性滿足Process級別的任務,從而被優先分配到非本地節點執行,如果的確沒有Executors在對應的節點上運行,或者的確是No Prefer的任務(如shuffleRDD),這樣做確實是比較優化的選擇,但是這裏的實際情況只是這部分Executors還沒來得及註冊上而已。這種情況下,即使加大本節中這幾個參數的數值也沒有幫助。針對這個情況,有一些已經完成的和正在進行中的PR通過例如動態調整No Prefer隊列,監控節點註冊比例等等方式試圖來給出更加智能的解決方案。不過,你也可以根據自身集羣的啓動情況,通過在創建SparkContext之後,主動Sleep幾秒的方式來簡單的解決這個問題。

 

 

spark.speculation

 

spark.speculation以及spark.speculation.interval,spark.speculation.quantile, spark.speculation.multiplier等參數調整Speculation行爲的具體細節,Speculation是在任務調度的時候,如果沒有適合當前本地性要求的任務可供運行,將跑得慢的任務在空閒計算資源上再度調度的行爲,這些參數調整這些行爲的頻率和判斷指標,默認是不使用Speculation的。

 

通常來說很難正確的判斷是否需要Speculation,能真正發揮Speculation用處的場合,往往是某些節點由於運行環境原因,比如CPU資源由於某種原因被佔用,磁盤損壞導致IO緩慢造成任務執行速度異常的情況,當然前提是你的分區任務不存在僅能被執行一次,或者不能同時執行多個拷貝等情況。Speculation任務參照的指標通常是其它任務的執行時間,而實際的任務可能由於分區數據尺寸不均勻,本來就會有時間差異,加上一定的調度和IO的隨機性,所以如果一致性指標定得過嚴,Speculation可能並不能真的發現問題,反而增加了不必要的任務開銷,定得過寬,大概又基本相當於沒用。

 

個人覺得,如果你的集羣規模比較大,運行環境複雜,的確可能經常發生執行異常,加上數據分區尺寸差異不大,爲了程序運行時間的穩定性,那麼可以考慮仔細調整這些參數。否則還是考慮如何排除造成任務執行速度異常的因數比較靠鋪一些。

 

當然,我沒有實際在很大規模的集羣上運行過Spark,所以如果看法有些偏頗,還請有實際經驗的XD指正。

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