一、任務佔用資源計算
executor佔用CPU = executor_instances * executor_cores * 10 * 0.8(0.1核)
executor佔用記憶體 = executor_instances * (executor.memory + max(executor.memoryOverhead, OffHeap.size) + executor.pyspark.memory)(GB)
其中,若引數未手動設定,會分配預設值。
也就是說,使用預設引數,每個executor就會分配4g + max(5g, 3.7g) + 6g = 15g的記憶體,對於一般任務已經足夠使用。
-- driver
spark.driver.cores 1
spark.driver.memory 4g
-- executor
spark.executor.cores 2
spark.executor.memory 4g
spark.executor.memoryOverhead 5g
spark.executor.pyspark.memory 6g
-- Bytes,約為3.7G
spark.memory.offHeap.size 4000000000
二、使用者需要關注的引數
如上,在使用預設配置時,每個executor就會分配15g記憶體,已經足夠一般任務使用。
所以使用者一般只需配置spark.executor.instances,spark.sql.shuffle.partitions,spark.default.parallelism即可。
如果配置後發現還是報OOM錯誤,可適當提高記憶體引數,重要引數含義見下方。
推薦配置:
spark.executor.instances 50
spark.sql.shuffle.partitions 300
spark.default.parallelism 300
三、重要引數含義
1、driver相關引數
driver實際申請記憶體大小計算公式:driver.memory + driver.memoryOverhead
(1)spark.driver.memory
driver程序(JVM使用)的記憶體數,一般(memory/cores >= 2g)
通用配置:4g
df.collect()會返回所有資料的list,但是這個方法會將所有資料pull到driver,所以在遇到driver爆記憶體時,可以注意這一點。引數driver.memory調高。
引數調優建議:Driver的記憶體通常來說不設定,或者設定1G左右應該就夠了。
(2)spark.driver.cores
預設1,driver程式使用的CPU核心數,若無過多driver單機處理操作,一般不需要配置
通用配置:2
(3)spark.driver.memoryOverhead
driver JVM堆外記憶體的大小,預設為max(384, 0.1 * spark.driver.memory)
此配置存在預設單位MB,因此直接配置數字或帶具體單位,最少1g
通用配置:1g
2、executor相關引數
(1)spark.executor.instances
設定spark作業executor的個數executor.instances * executor.cores為當前application內並行執行task數,需要根據spark.sql.shuffle.partitions判斷,一般保證executor.instances * executor.cores <= partitions / 2
通用配置:10
引數調優建議:每個Spark作業的執行一般設定50~100個左右的Executor程序比較合適,設定太少或太多的Executor程序都不好。設定的太少,無法充分利用叢集資源;設定的太多的話,大部分佇列可能無法給予充分的資源。
(2)spark.executor.memory
每個executor程序(JVM使用)的記憶體大小
預設配置:4g
引數調優建議:每個Executor程序的記憶體設定4G~8G較為合適。但是這只是一個參考值,具體的設定還是得根據不同部門的資源佇列來定。可以看看自己團隊的資源佇列的最大記憶體限制是多少,num-executors乘以executor-memory,是不能超過佇列的最大記憶體量的。此外,如果你是跟團隊裡其他人共享這個資源佇列,那麼申請的記憶體量最好不要超過資源佇列最大總記憶體的1/4~1/3,避免你自己的Spark作業佔用了佇列所有的資源,導致別的同學的作業無法執行
(3)spark.executor.cores
每個executor的core數目。每個core同一時間只能執行一個Task執行緒,cores的數目也就意味著每個executor並行task的數目。
每個task分配的記憶體大小是executor-memory/executor-cores,可以按照這個分析每個task所佔用的記憶體大小,一般(memory/cores >= 2g)。
每個executor為1個程序,分配一個JVM,考慮到JVM載入task資訊的數量,cores個數不要超過5,超出後會容易出現大量載入任務資訊導致OOM的情況。
預設配置:2
(4)spark.executor.memoryOverhead
executor JVM堆外記憶體大小,一般執行非JVM的邏輯
此部分記憶體主要用於JVM自身,字串, NIO Buffer(Driect Buffer)等開銷。此部分為使用者程式碼及Spark 不可操作的記憶體,不足時可透過調整引數解決。
此配置存在預設單位MB,因此直接配置數字或帶具體單位,最少1g
預設配置:5g
(5)spark.executor.pyspark.memory
python的worker記憶體,僅在使用pyspark時生效
預設配置:6g
(6)spark.shuffle.spill.numElementsForceSpillThreshold
預設256000000,即256M。
shuffle超過該資料會強行落盤此配置存在單位B,因此直接配置數字即可
通用配置:256000000
(7)spark.sql.shuffle.partitions
對Spark SQL專用的設定
預設200,用於設定shuffle時partition的數目,只作用於SQL、DataSet的join/aggregations,無法對純map操作生效。該引數代表了shuffle read task的並行度。
在使用者shuffle OOM時,可考慮增大數目
通用配置:200
(8)spark.default.parallelism
在處理RDD時才會起作用,對Spark SQL的無效
預設200,與上面作用相同,只作用於RDD的join/reduceByKey等,無法對純map操作生效。
通用配置:200
通常來說,Spark預設設定的數量是偏少的(比如就幾十個task),如果task數量偏少的話,就會導致你前面設定好的Executor的引數都前功盡棄。試想一下,無論你的Executor程序有多少個,記憶體和CPU有多大,但是task只有1個或者10個,那麼90%的Executor程序可能根本就沒有task執行,也就是白白浪費了資源!
Spark官網建議的設定原則是,設定該引數為num-executors * executor-cores的2~3倍較為合適,比如Executor的總CPU core數量為300個,那麼設定1000個task是可以的,此時可以充分地利用Spark叢集的資源。
(9)spark.memory.fraction
預設0.75,用於存放快取資料和執行資料,剩餘0.25為User Memory,存放使用者定義的資料結構和Spark後設資料資訊。
在使用者persist大量資料或者shuffle聚合資料量比較大時可以考慮增加該值
快取持久化(persist) + 執行(shuffle+執行編寫的程式碼) = memory.fraction,預設為0.75,persist用memory.storageFraction引數指定,預設0.5
(10)spark.memory.storageFraction(spark.memory.useLegacyMode(代表啟用spark1.6前的版本)時,spark.storage.memoryFraction)
預設0.5, storage記憶體大小,用於儲存快取資料,剩餘空間用於execute。
在Unified Memory Manage模式下,記憶體會自動調整,分配storage和execute使用,但是在storage記憶體不足時,會要回所有分配的記憶體。
在使用者shuffle處理資料比較大時可減小該引數
(11)spark.memory.offHeap.size
設定JVM堆外記憶體大小,可以執行executor JVM相關計算,預設為5000000000,即5G。
一般任務中只有部分shuffle需要大量操作,記憶體可能OOM時啟用,在spark.memory.offHeap.enabled設為true時啟用。
此配置存在單位B,因此直接配置數字即可
預設配置:4000000000