一. Spark做業原理
咱們使用spark-submit提交一個Spark做業以後,這個做業就會啓動一個對應的Driver進程。該進程是向集羣管理器(Yarn,K8s)申請運行Spark做業須要使用的資源,這裏的資源指的就是Executor進程。
YARN集羣管理器會根據咱們爲Spark做業設置的資源參數,在各個工做節點上,啓動必定數量的Executor進程,每一個Executor進程都佔有必定數量的內存和CPU core。
在申請到了做業執行所需的資源以後,Driver進程就會開始調度和執行咱們編寫的做業代碼了。
Driver進程會將咱們編寫的Spark做業代碼分拆爲多個stage,每一個stage執行一部分代碼片斷,併爲每一個stage建立一批task,而後將這些task分配到各個Executor進程中執行。
task是最小的計算單元,負責執行如出一轍的計算邏輯(也就是咱們本身編寫的某個代碼片斷),只是每一個task處理的數據不一樣而已。
一個stage的全部task都執行完畢以後,會在各個節點本地的磁盤文件中寫入計算中間結果,而後Driver就會調度運行下一個stage。
下一個stage的task的輸入數據就是上一個stage輸出的中間結果。如此循環往復,直到將咱們本身編寫的代碼邏輯所有執行完,而且計算完全部的數據,獲得咱們想要的結果爲止。
Spark是根據shuffle類算子來進行stage的劃分。若是咱們的代碼中執行了某個shuffle類算子(好比reduceByKey、join等),那麼就會在該算子處,劃分出一個stage界限來。
能夠大體理解爲,shuffle算子執行以前的代碼會被劃分爲一個stage,shuffle算子執行以及以後的代碼會被劃分爲下一個stage。
所以一個stage剛開始執行的時候,它的每一個task可能都會從上一個stage的task所在的節點,去經過網絡傳輸拉取須要本身處理的全部key,而後對拉取到的全部相同的key使用咱們本身編寫的算子函數執行聚合操做(好比reduceByKey()算子接收的函數)。這個過程就是shuffle。
當咱們在代碼中執行了cache/persist等持久化操做時,根據咱們選擇的持久化級別的不一樣,每一個task計算出來的數據也會保存到Executor進程的內存或者所在節點的磁盤文件中。
所以Executor的內存主要分爲三塊:
第一塊是讓task執行咱們本身編寫的代碼時使用,默認是佔Executor總內存的20%;
第二塊是讓task經過shuffle過程拉取了上一個stage的task的輸出後,進行聚合等操做時使用,默認也是佔Executor總內存的20%;
第三塊是讓RDD持久化時使用,默認佔Executor總內存的60%。
task的執行速度是跟每一個Executor進程的CPU core數量有直接關係的。一個CPU core同一時間只能執行一個線程。而每一個Executor進程上分配到的多個task,都是以每一個task一條線程的方式,多線程併發運行的。
若是CPU core數量比較充足,並且分配到的task數量比較合理,那麼一般來講,能夠比較快速和高效地執行完這些task線程。
二.核心調優參數
num-executors:
該參數用於設置Spark做業總共要用多少個Executor進程來執行。Driver在向YARN集羣管理器申請資源時,YARN集羣管理器會盡量按照你的設置來在集羣的各個工做節點上,啓動相應數量的Executor進程。這個參數很是之重要,若是不設置的話,默認只會給你啓動少許的Executor進程,此時你的Spark做業的運行速度是很是慢的。(建議50~100個左右的Executor進程)
executor-memory:
該參數用於設置每一個Executor進程的內存。Executor內存的大小,不少時候直接決定了Spark做業的性能,並且跟常見的JVM OOM異常,也有直接的關聯。(根據做業大小不一樣,建議設置4G~8G,num-executors乘以executor-memory,是不能超過隊列的最大內存量的)
executor-cores:
該參數用於設置每一個Executor進程的CPU core數量。這個參數決定了每一個Executor進程並行執行task線程的能力。由於每一個CPU core同一時間只能執行一個task線程,所以每一個Executor進程的CPU core數量越多,越可以快速地執行完分配給本身的全部task線程。(建議設置爲2~4個,且num-executors * executor-cores不要超過隊列總CPU core的1/3~1/2)
driver-memory:
該參數用於設置Driver進程的內存(建議設置512M到1G)。
spark.default.parallelism:
該參數用於設置每一個stage的默認task數量。這個參數極爲重要,若是不設置可能會直接影響你的Spark做業性能。(建議爲50~500左右,缺省狀況下Spark本身根據底層HDFS的block數量來設置task的數量,默認是一個HDFS block對應一個task。Spark官網建議設置該參數爲num-executors * executor-cores的2~3倍較爲合適)
spark.storage.memoryFraction:
該參數用於設置RDD持久化數據在Executor內存中能佔的比例,默認是0.6(原則上是儘量保證數據可以所有在內存中,但若是發現做業發生頻繁的GC,就該考慮是否調小)
spark.shuffle.memoryFraction:
該參數用於設置shuffle過程當中一個task拉取到上個stage的task的輸出後,進行聚合操做時可以使用的Executor內存的比例,默認是0.2。也就是說,Executor默認只有20%的內存用來進行該操做。shuffle操做在進行聚合時,若是發現使用的內存超出了這個20%的限制,那麼多餘的數據就會溢寫到磁盤文件中去,此時就會極大地下降性能。(shuffle操做較多時,建議下降持久化操做的內存佔比,提升shuffle操做的內存佔比比例,避免shuffle過程當中數據過多時內存不夠用,必須溢寫到磁盤上,下降了性能)
微信掃描二維碼,關注個人公衆號