資源影響因素
Spark和Yarn管理的資源限制因素:
- CPU
- 內存
- 磁盤
- 網絡I/O
Spark和Yarn管理的兩個主要資源為CPU和內存,剩下不會主動管理,所以設置資源也是主要通過這兩方面進行設置。
資源優化配置
在資源配置中,可以用過代碼的設置sparkconf進行設置,或者在腳本里通過參數進行設置,腳本的優先級大於代碼的優先級。YARN的相關屬性是
# 控制每個節點上的容器使用的最大內存總和。 yarn.nodemanager.resource.memory-mb # 控制每個節點上的容器使用的最大內核總數。 yarn.nodemanager.resource.cpu-vcores
YARN請求的內存時,需要注意一下兩點:
--executor-memory/spark.executor.memory
控制執行程序堆的大小,但是JVM也可以在堆外使用一些內存,例如,spark.yarn.executor.memoryOverhead 用於內聯的String和直接字節緩沖區。該屬性的值將添加到執行程序內存中,以確定每個執行程序對YARN的完整內存請求。默認為max(384,.07 * spark.executor.memory)。yarn.scheduler.minimum-allocation-mb
yarn.scheduler.increment-allocation-mb
在確定Spark執行程序的大小時要注意的最后幾個問題:
- 應用程序主服務器是一個非執行程序容器,具有從YARN請求容器的特殊功能,它占用必須自己預算的資源。在yarn-client模式下,它默認為1024MB和一個vcore。在yarn-cluster模式下,應用程序主控程序運行驅動程序,因此使用和屬性支持其資源通常很有用。
--driver-memory
--driver-cores
- 運行內存過多的執行程序通常會導致過多的垃圾回收延遲(當內存不足時會觸發JVM回收內存)。對於單個執行器來說,64GB是一個不錯的上限。
- executor遇到大量並發線程效率會降低。官方的建議是,每個executor最多可以設置五個task,以實現較高效率的寫入吞吐量,因此最好將每個executor的vcores保持在該數量以下。
- 啟動太多executor執行比較少任務的時候,會拋棄同個executor起多線程的效率,反而降低效率。例如,廣播變量需要在每個executor上復制一次,因此太多executor將產生更多的副本成本。
腳本模板
./spark-submit \ --class 入口類 --master yarn-cluster \ --num-executors 100 \ --executor-memory 6G \ --executor-cores 4 \ --driver-memory 1G \ --conf spark.default.parallelism=1000 \ --conf spark.storage.memoryFraction=0.5 \ --conf spark.shuffle.memoryFraction=0.3 \ jar包路徑 參數
num-executors
參數說明:該參數用於設置Spark作業總共要用多少個Executor進程來執行。Driver在向YARN集群管理器申請資源時,YARN集群管理器會盡可能按照你的設置來在集群的各個工作節點上,啟動相應數量的Executor進程。不設置默認值是2。
參數調優建議:根據從節點的數據量設置對應的數量,設置太多會讓一台節點起多個executor,影響機器的性能,起太少會讓節點空閑。
executor-memory
參數說明:該參數用於設置每個Executor進程的內存。Executor內存的大小,很多時候直接決定了Spark作業的性能,而且跟常見的JVM OOM異常,也有直接的關聯。
參數調優建議:具體情況得根據不同部門的資源隊列和程序部署的情況來決定,數據源大小也是一個參考點;但num-executors * executor-memory,是不能超過隊列的最大內存量的。此外,如果資源是和別人共享的情況或者多程序的情況,那么申請的內存量最好不要超過資源隊列最大總內存的1/3~1/2,避免你自己的Spark作業占用了隊列所有的資源,導致別的同事的作業無法運行。
executor-cores
參數說明:該參數用於設置每個Executor進程的CPU core數量。這個參數決定了每個Executor進程並行執行task線程的能力。因為每個CPU core同一時間只能執行一個task線程,因此每個Executor進程的CPU core數量越多,越能夠快速地執行完分配給自己的所有task線程。
參數調優建議:每個Executor同時執行5個task時HDFS的寫的吞吐量是最好的。所以,建議將executor-cores
的值為3-5。這個限制也要根據資源情況來限定,例如資源隊列里最大的CPU core數量,假設你的executor的內存沒辦法配置太大,那么executor-cores不能配置太多,不然會導致內存不夠而引起內存溢出。
driver-memory
參數說明:該參數用於設置Driver進程的內存。
參數調優建議:Driver的內存通常來說不設置,或者設置1G左右應該就夠了。但你的程序有使用collect算子將RDD的數據全部拉取到Driver上進行處理(或者是用map side join操作),那么必須確保Driver的內存足夠大,否則會出現OOM內存溢出的問題。
spark.default.parallelism
參數說明:該參數用於設置每個stage的默認task數量,也可以認為是分區數。
參數調優建議:Spark官網的建議是,設置該參數為num-executors * executor-cores的2~3倍較為合適;如果不設置這個參數,那么會導致Spark自己根據底層HDFS的block數量來設置task的數量,默認是一個HDFS block對應一個task。通常來說,Spark默認設置的數量是偏少的(比如就幾十個task),如果task數量偏少的話,就會導致你前面設置好的Executor的cpu閑置,浪費資源。
spark.storage.memoryFraction
參數說明:該參數用於設置RDD持久化數據在Executor內存中能占的比例,默認是0.6。也就是說,默認Executor 60%的內存,可以用來保存持久化的RDD數據。根據你選擇的不同的持久化策略,如果內存不夠時,可能數據就不會持久化,或者數據會寫入磁盤。
參數調優建議:如果Spark作業中,有較多的RDD持久化操作,該參數的值可以適當提高一些,保證持久化的數據能夠容納在內存中。避免內存不夠緩存所有的數據,導致數據只能寫入磁盤中,降低了性能。但是如果Spark作業中的shuffle類操作比較多,而持久化操作比較少,那么這個參數的值適當降低一些比較合適。此外,如果發現作業由於頻繁的gc導致運行緩慢(通過spark web ui可以觀察到作業的gc耗時),意味着task執行用戶代碼的內存不夠用,那么同樣建議調低這個參數的值。
spark.shuffle.memoryFraction
參數說明:該參數用於設置shuffle過程中一個task拉取到上個stage的task的輸出后,進行聚合操作時能夠使用的Executor內存的比例,默認是0.2。也就是說,Executor默認只有20%的內存用來進行該操作。shuffle操作在進行聚合時,如果發現使用的內存超出了這個20%的限制,那么多余的數據就會溢寫到磁盤文件中去,此時就會極大地降低性能。
參數調優建議:如果Spark作業中的RDD持久化操作較少,shuffle操作較多時,建議降低持久化操作的內存占比,提高shuffle操作的內存占比比例,避免shuffle過程中數據過多時內存不夠用,必須溢寫到磁盤上,降低了性能。此外,如果發現作業由於頻繁的gc導致運行緩慢,意味着task執行用戶代碼的內存不夠用,那么同樣建議調低這個參數的值。
配置參數
假設集群服務器配置:
節點:10 核數:16/台 內存:64GB/台
配置一:使用較小的executors
這里起和核數相同的executor
--num-executors = 16 x 10 = 160 --executor-cores = 1 (one executor per core) --executor-memory = `mem-per-node/num-executors-per-node` = 64GB/16 = 4GB
由於每個executor只分配了一個核,我們將無法利用在同一個JVM中運行多個任務的優點。 此外,共享/緩存變量(如廣播變量和累加器)將在節點的每個核心中復制16次。 最嚴重的就是,我們沒有為Hadoop / Yarn守護程序進程留下足夠的內存開銷,我們還忘記了將ApplicationManagers運行所需要的開銷加入計算。
配置二:使用較大的executors
每個executor都有16個核
--num-executors = 10 --executor-cores = 16 --executor-memory = `mem-per-node/num-executors-per-node` = 64GB/1 = 64GB
由於HDFS客戶端遇到大量並發線程會出現一些bug,即HDFS吞吐量會受到影響。同時過大的內存分配也會導致過多的GC延遲。
配置三:使用優化的executors
--num-executors = 29 --executor-cores = 5 --executor-memory = 19GB
(1)給每個Executor分配5個core即executor-cores=5
,這樣對HDFS的吞吐量會比較友好。
(2)為后台進程留一個core,則每個節點可用的core數是16 - 1 = 15
。所以集群總的可用core數是15 x 10 = 150
。
(3)每個節點上的Executor數就是 15 / 5 = 3
,集群總的可用的Executor數就是 3 * 10 = 30
。為ApplicationManager
留一個Executor,則num-executors=29
。
(4)每個節點上每個Executor可分配的內存是 (64GB-1GB) / 3 = 21GB
(減去的1GB是留給后台程序用),除去MemoryOverHead=max(384MB, 7% * 21GB)=2GB
,所以executor-memory=21GB - 2GB = 19GB
。
此方法既保證了在一個JVM實例里能同時執行task的優勢,也保證了hdfs的吞吐量。
配置四:在配置三基礎上再優化
按照方法三,每個Executor分配到的內存是19GB,假設10GB內存就夠用了。那么此時我們可以將executor-cores
降低如降低為3,那么每個節點就可以有15 / 3 = 5
個Executor,那么總共可以獲得的Executor數就是 (5 * 10) - 1 =49
,每個節點上每個Executor可分配的內存是(64GB-1GB) / 5 = 12GB
,除去MemoryOverHead=max(384MB, 7% * 12GB)=1GB
,所以executor-memory=12GB - 1GB = 11GB
所以最后的參數配置是
--num-executors = 49 --executor-cores = 3 --executor-memory = 11GB
這里只是做一個參考,具體的實際情況依照個人的資源情況,參考有數據量大小,部門的資源使用情況等。一切以實際為准,這里只是搬運整理下資源以便翻閱。
參考博客:https://www.cnblogs.com/arachis/p/spark_parameters.html http://www.dzjqx.cn/news/show-28638.html https://blog.cloudera.com/how-to-tune-your-apache-spark-jobs-part-2/