提交Spark作业 | 科学设定spark-submit参数
点击上方蓝色字体,选择“设为星标”
回复”资源“获取更多惊喜
首先摆出我们常用的一种设定。
bin/spark-submit \
--class com.xyz.bigdata.calendar.PeriodCalculator \
--master yarn \
--deploy-mode cluster \
--queue default_queue \
--num-executors 50 \
--executor-cores 2 \
--executor-memory 4G \
--driver-memory 2G \
--conf "spark.default.parallelism=250" \
--conf "spark.shuffle.memoryFraction=0.3" \
--conf "spark.storage.memoryFraction=0.5" \
--conf "spark.driver.extraJavaOptions=-XX:+UseG1GC" \
--conf "spark.executor.extraJavaOptions=-XX:+UseG1GC" \
--verbose \
${PROJECT_DIR}/bigdata-xyz-0.1.jar
关于spark-submit的执行过程,读Spark Core的源码能够获得一个大致的印象。
num-executors
含义:设定Spark作业要用多少个Executor进程来执行。
设定方法:根据我们的实践,设定在30~100个之间为最佳。如果不设定,默认只会启动非常少的Executor。如果设得太小,无法充分利用计算资源。设得太大的话,又会抢占集群或队列的资源,导致其他作业无法顺利执行。
executor-cores
含义:设定每个Executor能够利用的CPU核心数(这里核心指的是vCore)。核心数越多,并行执行Task的效率也就越高。
设定方法:根据我们的实践,设定在2~6之间都是可以的,主要是根据业务类型和数据处理逻辑的复杂程度来定,一般来讲设2或者3就够用了。需要注意的是,num-executors * executor-cores不能将队列中的CPU资源耗尽,最好不要超过总vCore数的1/3,以给其他作业留下剩余资源。
executor-memory
含义:设定每个Executor的内存量(堆内内存)。这个参数比executor-cores更为重要,因为Spark作业的本质就是内存计算,内存的大小直接影响性能,并且与磁盘溢写、OOM等都相关。
设定方法:一般设定在2G~8G之间,需要根据数据量慎重做决定。如果作业执行非常慢,出现频繁GC或者OOM,就得适当调大内存。并且与上面相同,num-executors * executor-memory也不能过大,最好不要超过队列总内存量的一半。
另外,还有一个配置项spark.executor.memoryOverhead,用来设定每个Executor可使用的堆外内存大小,默认值是executor-memory的0.1倍,最小值384M。一般来讲都够用,不用特意设置。
driver-memory
含义:设定Driver进程的内存量(堆内内存)。
设定方法:由于我们几乎不会使用collect()之类的算子把大量RDD数据都拉到Driver上来处理,所以它的内存可以不用设得过大,2G可以应付绝大多数情况。但是,如果Spark作业处理完后数据膨胀比较多,那么还是应该酌情加大这个值。与上面一项相同,spark.driver.memoryOverhead用来设定Driver可使用的堆外内存大小。
spark.default.parallelism
含义:对于shuffle算子,如reduceByKey()或者join(),这个参数用来指定父RDD中最大分区数。由于分区与Task有一一对应关系,因此也可以理解为Task数。其名称的字面意义是“并行度”,不能直接表达出这种含义。
设定方法:Spark官方文档中推荐每个CPU core执行23个Task比较合适,因此这个值要设定为(num-executors * executor-cores)的23倍。这个参数同样非常重要,因为如果不设定的话,分区数就会由RDD本身的分区来决定,这样往往会使得计算效率低下。
spark.shuffle.memoryFraction
含义:shuffle操作(聚合、连接、分组等等)能够使用的可用堆内存(堆大小减去300MB保留空间)的比例,默认值是0.2。如果shuffle阶段使用的内存比例超过这个值,就会溢写到磁盘。
设定方法:取决于计算逻辑中shuffle逻辑的复杂度,如果会产生大量数据,那么一定要调高。在我们的实践中,一般都设定在0.3左右。但是,如果调太高之后发现频繁GC,那么就是执行用户代码的execution内存不够用了,适当降低即可。
spark.storage.memoryFraction
含义:缓存操作(persist/cache)能够使用的可用堆内存的比例,默认值是0.6。
设定方法:如果经常需要缓存非常大的RDD,那么就需要调高。否则,如果shuffle操作更为重量级,适当调低也无妨。我们一般设定在0.5左右。
其实,spark.shuffle/storage.memoryFraction是旧版的静态内存管理(StaticMemoryManager)的遗产。在Spark 1.6版本之后的文档中已经标记成了deprecated。目前取代它们的是spark.memory.fraction和spark.memory.storageFraction这两项,参考新的统一内存管理(UnifiedMemoryManager)机制可以得到更多细节。前者的含义是总内存占堆的比例,即execution+storage+shuffle内存的总量。后者则是storage内存占前者的比例。默认值分别为0.75(最新版变成了0.6)和0.5。
spark.driver/executor.extraJavaOptions
含义:Driver或Executor进程的其他JVM参数。
设定方法:一般可以不设置。如果设置,常见的情景是使用-Xmn加大年轻代内存的大小,或者手动指定垃圾收集器(最上面的例子中使用了G1,也有用CMS的时候)及其相关参数。
一句话总结 spark-submit参数的设定有一定的准则可循,但更多地是根据实际业务逻辑和资源余量进行权衡。