Spark性能优化主要分为:
- 开发调优
- 资源调优
- 数据倾斜调优
- 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内存溢出的问题
我们的生产配置:
- spark.driver.cores 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内存。
- storage内存:存储 broadcast,cache,persist数据
- execution内存:执行内存,join、aggregate、map等 shuffle中间结果都缓存在这部分内存中,满了再写入磁盘,能够减少IO,其实map过程也是在这个内存中执行的
- 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的情况