Spark --如何合理地設置executor-memory、executor-cores、num-executors

參數介紹

executor-memory 表示分配給每個executor的內存,默認是1G。
executor-cores 表示分配給每個executor的core數即核數,在spark on yarn模式下默認是1。
num-executors 表示爲當前application啓動的executor的數目。如果開啓了動態分配(spark.dynamicAllocation.enabled默認是false),則初始啓動的executor的數目就是num-executors設置的數目。

以下4點建議需要牢記

  • Hadoop/Yarn/OS Deamons:當我們在cluster manager如yarn集羣上跑spark任務時,後臺會有一些進程在執行如NameNode、Secondary NameNode、DataNode、ResourceManager、NodeManager等。所以在確定上面3個參數前,我們需要確保爲那些後臺進程留下足夠的cores(一般每個節點留一個core)。
  • Yarn ApplicationMaster (AM)ApplicationMaster負責從ResourceManager申請資源並且和NodeManagers一起執行和監控containers和它們的資源消耗。如果我們是spark on yarn模式,那麼我們需要爲ApplicationMaster預留一些資源(1G和1個Executor)。
  • HDFS Throughput:HDFS client會有多線程的併發問題。研究表明每個Executor同時執行5個task時HDFS的寫的吞吐量是最好的。所以,建議將executor-cores的值保持在5或5以下
  • MemoryOverhead: 我們以spark on yarn且deploy-mode=cluster爲例。在2.3版本前,
    是通過spark.yarn.executor.memoryOverhead來定義executor的memoryOverhead的。官方給的比例是10%,一般是在6%到10%之間,大多數都選擇了7%。具體可參考1. 6.3-spark-properties
Full memory requested to yarn per executor =
          spark-executor-memory + spark.yarn.executor.memoryOverhead.
 spark.yarn.executor.memoryOverhead = 
        	Max(384MB, 7% of spark-executor-memory)

在2.3版本後,是用spark.executor.memoryOverhead來定義的。其中memoryOverhead是用於VM overheads, interned strings, other native overheads等。

執行的Executor分配有很大內存時通常會造成很大的GC延遲。
執行的Executor很小(如一個core和運行一個task所需要的足夠的內存),這樣在一個JVM裏(每個Executor就是一個獨立的JVM實例)能同時運行多個task的優勢就丟失了。

配置參數

首先假設集羣配置如下

10 Nodes
16 cores per Node
64GB RAM per Node

方法一:Tiny executors(One Executor per core)

--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

在這種One Executor per core的配置下,我們無法利用在一個JVM裏同時運行多個task的優勢。同時,共享變量如廣播變量(broadcast variables)和累加器(accumulators)會在每個節點上的每個core上覆制一次即每個節點上會複製16次(因爲共享變量會複製到每個executor上)。而且我們也沒有預留足夠的資源給後臺進程如NameNode等,也沒有把ApplicationManager的資源算上去。非常不好!!!

方法二:Fat executors (One Executor per node)

--num-executors =  10
--executor-cores = 16 
--executor-memory = `mem-per-node/num-executors-per-node`
                     = 64GB/1 = 64GB

每個Executor獲取到節點上的所有core,除開ApplicationManager和後臺進程不談,HDFS的吞吐量也會收到影響且會伴隨着驗證的GC。同樣也是非常不好的!!!

方法三:Balance between Fat (vs) Tiny

第一,根據上面的參數建議,我們給每個Executor分配5個core即executor-cores=5,這樣對HDFS的吞吐量會比較友好。
第二,爲後臺進程留一個core,則每個節點可用的core數是16 - 1 = 15。所以集羣總的可用core數是15 x 10 = 150
第三,每個節點上的Executor數就是 15 / 5 = 3,集羣總的可用的Executor數就是 3 * 10 = 30 。爲ApplicationManager留一個Executor,則num-executors=29
第四,每個節點上每個Executor可分配的內存是 (64GB-1GB) / 3 = 21GB(減去的1GB是留給後臺程序用),除去MemoryOverHead=max(384MB, 7% * 21GB)=2GB,所以executor-memory=21GB - 2GB = 19GB
所以最後的參數配置是

--num-executors =  29
--executor-cores = 5 
--executor-memory = 19GB

此方法既保證了在一個JVM實例裏能同時執行task的優勢,也保證了hdfs的吞吐量。

方法四:在方法三基礎上每個executor不需要這麼多內存

按照方法三,每個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

參考網址

distribution_of_executors_cores_and_memory_for_spark_application
how-to-tune-spark-executor-number-cores-and-executor-memory
resource-allocation-configuration-spark-yarn

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