Spark性能优化主要分为:

  1. 开发调优
  2. 资源调优
  3. 数据倾斜调优
  4. shuffle调优

在开发完Spark作业之后,就该为作业配置合适的资源了

资源参数设置的不合理,可能会导致没有充分利用集群资源,作业运行会极其缓慢;或者设置的资源过大,队列没有足够的资源来提供,进而导致各种异常

本篇罗列资源调优的注意事项

1. 引言

建议先了解 Spark作业基本运行原理 和 Spark内存模型
参考:

尤其注意区分 Spark1.6.0 之后 内存默认为统一管理(Unified Memory Manager)方式,不再是 静态管理(Static Memory Manager)方式,在针对内存资源做参数配置是要注意区分 当前的内存管理策略


2. 资源参数调优

2.1 num-executors

参数说明:该参数用于设置 Spark 作业总共要用多少个 Executor 进程来执行

Driver在向YARN集群管理器申请资源时,YARN集群管理器会尽可能按照你的设置来在集群的各个工作节点上,启动相应数量的Executor进程

这个参数非常重要,如果不设置的话,默认只会给你启动少量的Executor进程,此时你的Spark作业的运行速度是非常慢的

参数调优建议:根据业务复杂性和现有资源情况设置(50~100个左右),设置太少或太多的Executor进程都不好。设置的太少,无法充分利用集群资源;设置的太多的话,大部分队列可能无法给予充分的资源


2.2 executor-memory

参数说明:该参数用于设置每个Executor进程的内存

Executor内存的大小,很多时候直接决定了Spark作业的性能,而且跟常见的JVM OOM异常,也有直接的关联。

参数调优建议:每个Executor进程的内存设置4G~8G较为合适。

但是这只是一个参考值,具体的设置还是得根据不同部门的资源队列来定

可以看看自己团队的资源队列的最大内存限制是多少,num-executors乘以executor-memory,是不能超过队列的最大内存量的

此外,如果你是跟团队里其他人共享这个资源队列,那么申请的内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业占用了队列所有的资源,导致别人的作业无法运行


2.3 executor-cores

参数说明:该参数用于设置每个Executor进程的CPU core数量

这个参数决定了每个Executor进程并行执行task线程的能力

因为每个CPU core同一时间只能执行一个task线程,因此每个Executor进程的CPU core数量越多,越能够快速地执行完分配给自己的所有task线程

参数调优建议:Executor的CPU core数量设置为2~4个较为合适

同样得根据不同部门的资源队列来定,可以看看自己的资源队列的最大CPU core限制是多少,再依据设置的Executor数量,来决定每个Executor进程可以分配到几个CPU core

同样建议,如果是跟他人共享这个队列,那么num-executors * executor-cores不要超过队列总CPU core的1/3~1/2左右比较合适,也是避免影响其他同学的作业运行。最好的应该就是一个cpu core对应两到三个task(官方建议


2.4 driver-memory

参数说明:该参数用于设置Driver进程的内存

参数调优建议:Driver的内存通常来说不设置,或者设置1G左右应该就够了

唯一需要注意的一点是,如果需要使用collect算子将RDD的数据全部拉取到Driver上进行处理,那么必须确保Driver的内存足够大,否则会出现OOM内存溢出的问题

我们的生产配置:

  1. spark.driver.cores 2
  2. spark.driver.memory 2048m

2.5 spark.default.parallelism

参数说明:该参数用于设置每个stage的默认task数量

这个参数极为重要,如果不设置可能会直接影响你的Spark作业性能

一个分区对应一个task,也就是这个参数其实就是设置task的数量

参数调优建议:num-executors * executor-cores的2~3倍(官网)


2.6 storage内存、execution内存、other内存分配配置

Spark在一个executor中的内存分为3块:storage内存、execution内存、other内存。

  1. storage内存:存储 broadcast,cache,persist数据
  2. execution内存:执行内存,join、aggregate、map等 shuffle中间结果都缓存在这部分内存中,满了再写入磁盘,能够减少IO,其实map过程也是在这个内存中执行的
  3. other内存:程序代码执行时预留给自己的内存

其中,execution 和 storage 是Spark的 Executor 中内存的占用大户,other占用内存相对少很多

【spark1.6.0之前版本】

spark1.6.0之前版本,execution 和 storage 的内存分配是独立配置的,使用的参数配置分别是:

spark.storage.memoryFraction:storage内存占Executor总内存比例,default 0.6
spark.shuffle.memoryFraction:execution内存占Executor总内存比例,default 0.2

spark1.6.0之前版本,上述两块内存是互相隔离的,无法空闲借用。这就导致了Executor的内存利用率不高,而且需要根据Application的具体情况,使用者自己来调节这两个参数优化Spark的内存使用。

【spark1.6.0及之后版本】

spark1.6.0及之后版本,execution内存和storage内存支持合并配置,使用的参数配置分别是:

spark.memory.fraction:“execution内存+storage内存” 占Executor总内存比例,default 0.6
spark.memory.storageFraction:storage内存 默认 占Executor总内存比例,default 0.5

storage内存如果运行时不够用,且execution内存有空闲,可以借用execution内存,反之不行

execution内存 和 storage内存 可以相互借用,提高了内存的Spark中内存的使用率,同时也减少了OOM的情况