前言
本篇的主要阐述了Spark 各个参数的使用场景,以及使用的说明与参考;其实主要就是对 Spark 运行过程中各个使用资源的地方,通过调节各种参数来优化资源使用的效率,从而提升Spark作业的执行性能。首先通过大致的 Spark 任务提交流程了解,然后再介绍说明 Spark 各个参数的使用方式,这样更有利于熟悉了解。
Spark Job 提交
Spark Job 提交流程
spark-submit
提交 Spark 作业之后,就会启动一个对应的 Driver;不同运行模式 Driver 可能在本地启动,也有可能在集群中某个节点启动;- Driver 开始向集群资源管理器(YARN)申请 Executor 进程,其中包括一定数量 CPU core 与内存(根据 Spark 作业设置的参数分配资源);
- 申请到资源之后 Driver 就会开始调度、执行 Spark 作业代码,并将 Spark 作业代码拆分成多个 Stage(链接:stage划分原理);一个 Stage 会创建多个 Task ,这些 Task 会分配到各个节点的 Executor 进程中执行( Task 是最小的计算单元,同一类的 Task 负责计算 Stage 中相同逻辑代码,每一个处理的数据会不一样);
- Executor 的内存主要分为三部分:第一部分是让 Task 执行 Spark 作业代码使用(默认是占 Executor总内存的 20%);第二部分是 Task 通过 Shuffle 过程拉取了上一个 Stage 的 Task 输出结果,进行聚合等操作时使用(默认也是占 Executor 总内存的 20%);第三部分是 RDD 持久化时使用(默认占 Executor 总内存的 60%);
- 所有 Stage 执行完成之后,Driver 就会将结果返回;
Spark Job提交参数说明
./bin/spark-submit \
--master yarn-cluster \
--name SparkTast \
--class com.lh.SparkTast \
--num-executors 100 \
--executor-memory 6G \
--executor-cores 4G \
--driver-momory 1G \
--conf spark.default.parallelism=1000 \
--conf spark.storage.memoryFraction=0.5 \
--conf spark.shuffle.memoryFraction=0.5 \
num-executors:该作业总共需要多少executor进程执行
#建议:每个作业运行一般设置5,10,20个左右较合适
executor-memory:设置每个executor进程的内存, num-executors * numexecutors 代表作业申请的总内存量(尽量不要超过最大总内存的1/3~1/2)
#建议:设置5G~10G较合适
executor-cores:每个executor进程的CPU Core 3数量[1-5],该参数决定每个 executor进程并行执行task线程的能力, num-executors* executor-cores代表 作业申请总 CPU core数(不要超过总 CPU Core的 1/3~1/2 )
#建议:设置2~4个比较合适
driver-memory:设置Driver进程的内存
#建议:通常不用设置,一般 4G 就够了,若出现使用 collect 算子将 RDD 数据全部拉取到 Driver 上处理,就必须确 保该值足够大,否则 OOM 内存溢出(如果设置了广播变量再设置大一点)。
spark.default.parallelism:每个 stage 的默认 task 数量
#建议:设置 500~1000 较合适,默认一个 HDFS 的 block 对应一个 task,Spark默认值偏少,这样导致不能充分利用资源
spark.shuffle.memoryFraction(默认值:0.2)
#建议:如果持久化操作较少,但 shuffle 较多时,可以降低持久化内存占比,提高 shuffle 操作内存占比(提高执行效率)。
应用程序参数
num-executors
参数说明:该参数用于设置 Spark Job 需要多少个 Executor 进程运行,Driver 在向 YARN 集群管理器申请资源时,YARN 集群管理器会在集群的各个工作节点上,启动相应数量的 Executor 进程(默认会设置少量的)。
参数调优建议:每个 Spark 作业的运行一般设置 50~100 个左右的 Executor 进程比较合适,设置的太少,无法充分利用集群资源;设置的太多的话,大部分队列可能无法给予充分的资源(上100G的话设置100~200)。
executor-memory
参数说明:该参数用于设置每个 Executor 进程的内存,Executor 内存的大小,很多时候直接决定了 Spark 作业的性能,与常见的 JVM OOM 异常有直接的关联。
参数调优建议:每个 Executor 进程的内存设置 4G/8G 较为合适,num-executors * executor-memory 代表 Spark 作业申请到的总内存量(所有Executor进程的内存总和),总量不能超过队列的最大内存量。如果你是跟团队里其他人共享这个资源队列,那么申请的总内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业占用了队列所有的资源,导致别的 Spark 作业无法运行。
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左右比较合适,避免影响其他 Spark 作业运行。
driver-memory
参数说明:该参数用于设置Driver进程的内存。
参数调优建议:Driver的内存通常来说不设置,或者设置 1G 左右,如果需要使用 collect 算子将 RDD 的数据全部拉取到 Driver 上进行处理,那么必须确保 Driver 的内存足够大,否则会出现 OOM 内存溢出的问题。
spark.driver.cores
默认值:1
参数说明:Driver端分配的核数。
调优建议:Thriftserver是启动 Thriftserver服务的机器,资源充足的话可以尽量给多。
spark.driver.memory
默认值:1G
参数说明:Driver端分配的内存数。在 Client 模式下不能直接通过 SparkConf配置(JVM驱动已经启动了),但是可以通过 --driver-memory
来设置。
spark.executor.memory
默认值:1G
参数说明:每个Executor分配的内存数。
调优建议:会受到yarn CDH的限制,和MemoryOverhead相加不能超过总内存限制
spark.driver.maxResultSize
默认值:1G(最小1MB,设置0为无限)
参数说明:Driver端接收的最大结果大小,如果总大小超过此限制作业会被终止。
调优建议:不建议设置的太大,如果要做数据可视化,更应该控制在20-30MB以内。(过大会导致OOM)
spark.extraListeners
默认值:none
参数说明:随着SparkContext被创建而创建,用于监听单参数、无参数构造函数的创建,并抛出异常(实现 SparkListener 的类的逗号分隔列表)。
Shuffle 过程参数
spark.shuffle.file.buffer
默认值:32k
参数说明:该参数用于设置 shuffle write task 的 BufferedOutputStream 的 buffer 缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。
调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64k),从而减少 shuffle write 过程中溢写磁盘文件的次数,也就可以减少磁盘 IO 次数,进而提升性能。在实践中发现,合理调节该参数,性能会有 1%~5% 的提升。
spark.shuffle.io.maxRetries
默认值:3
参数说明:shuffle read task 从 shuffle write task 所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功,就可能会导致作业执行失败。
调优建议:对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次),以避免由于JVM 的full gc或者网络不稳定等因素导致的数据拉取失败。在实践中发现,对于针对超大数据量(数十亿~上百亿)的shuffle过程,调节该参数可以大幅度提升稳定性。
spark.shuffle.io.retryWait
默认值:5s
参数说明:具体解释同上,该参数代表了每次重试拉取数据的等待间隔。
调优建议:建议加大间隔时长(比如60s),以增加shuffle操作的稳定性。
spark.shuffle.sort.bypassMergeThreshold
默认值:200
参数说明:当 ShuffleManager 为 SortShuffleManager 时,如果 shuffle read task 的数量小于这个阈值(默认是200),则 shuffle write 过程中不会进行排序操作,而是直接按照未经优化的 HashShuffleManager 的方式去写数据,但是最后会将每个 task 产生的所有临时磁盘文件都合并成一个文件,并会创建单独的索引文件。
调优建议:使用 SortShuffleManager 时,如果的确不需要排序操作,那么建议将这个参数调大一些,大于 shuffle read task 的数量。那么此时就会自动启用 bypass机制,map-side 就不会进行排序了,减少了排序的性能开销。但是这种方式下,依然会产生大量的磁盘文件,因此shuffle write性能有待提高。
spark.shuffle.consolidateFiles
默认值:false
参数说明:如果使用 HashShuffleManager,该参数有效。如果设置为 true,那么就会开启 consolidate 机制,会大幅度合并shuffle write的输出文件,对于shuffle read task 数量特别多的情况下,这种方法可以极大地减少磁盘IO开销,提升性能。
调优建议:如果的确不需要 SortShuffleManager 的排序机制,那么除了使用 bypass机制,还可以尝试将 spark.shffle.manager 参数手动指定为hash,使用 HashShuffleManager,同时开启 consolidate 机制。在实践中尝试过,发现其性能比开启了 bypass 机制的 SortShuffleManager 要高出 10%~30%。
spark.shuffle.compress
默认值:true
参数说明:是否压缩map输出文件。
spark.shuffle.spill.compress
默认值:true
参数说明:shuffle过程中溢出的文件是否压缩(压缩方式:spark.io.compression.codec)
spark.shuffle.manager
默认值:sort
参数说明:该参数用于设置ShuffleManager的类型。Spark 1.5以后,有三个可选项:hash、sort 和 tungsten-sort。HashShuffleManager 是Spark 1.2以前的默认选项,但是Spark 1.2以及之后的版本默认都是SortShuffleManager了。tungsten-sort 与 sort类似,但是使用了 tungsten 计划中的堆外内存管理机制,内存使用效率更高。
调优建议:由于 SortShuffleManager 默认会对数据进行排序,因此如果你的业务逻辑中需要该排序机制的话,则使用默认的 SortShuffleManager;而如果你的业务逻辑不需要对数据进行排序,那么建议参考后面的几个参数调优,通过 bypass 机制或优化的HashShuffleManager 来避免排序操作,同时提供较好的磁盘读写性能。
spark.shuffle.memoryFraction
默认值:0.2
参数说明:该参数用于设置 shuffle 过程中一个 task 拉取到上个 stage 的 task 的输出后,进行聚合操作时能够使用的 Executor 内存的比例,默认是0.2。Executor 默认只有 20% 的内存用来进行该操作;shuffle 操作在进行聚合时,如果发现使用的内存超出了 20% 的限制,那么多余的数据就会溢写到磁盘文件中去,此时就会极大地降低性能。
参数调优建议:如果 Spark 作业中的 RDD 持久化操作较少,shuffle 操作较多时,建议降低持久化操作的内存占比,提高shuffle操作的内存占比比例,避免shuffle过程中数据过多时内存不够用,必须溢写到磁盘上,降低了性能。如果发现作业由于频繁的 GC导致运行缓慢,意味着 task 执行用户代码的内存不够用,那么同样建议调低这个参数的值。
spark.reducer.maxSizeInFlight
默认值:48m
参数说明:该参数用于设置 shuffle read task 的 buffer 缓冲大小, buffer 缓冲决定了每次能够拉取多少数据。从每个 reduce 任务同时拉取的最大 map 数,每个 reduce 都会在完成任务后,需要一个堆外内存的缓冲区来存放结果,如果没有充裕的内存就尽可能把这个调小一点。相反,堆外内存充裕,调大些就能节省 GC时间。
调优建议:如果作业可用的内存资源较为充足的话,可以适当增加参数的大小(比如 96M),从而减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能。在实践中发现,合理调节该参数,性能会有 1%~5% 的提升。
缺点:容易出现 reduce oom,reduce task 去 map 拉取数据,reduce 一边拉数据一边聚合 reduce段有一块聚合内存(executor memory * 0.2)
解决办法:
- 增加reduce 聚合的内存的比例 设置spark.shuffle.memoryFraction;
- 增加executor memory的大小 --executor-memory 5G;
- 减少reduce task每次拉取的数据量 设置spark.reducer.maxSizeInFlight 24m;
spark.reducer.maxBlocksInFlightPerAddress
默认值:Int.MaxValue
参数说明:限制了每个主机每次Reduce可以被多少台远程主机拉取文件块,调低这个参数可以有效减轻Node Manager的负载。
spark.reducer.maxReqsInFlight
默认值:Int.MaxValue
参数说明:限制远程机器拉取本机器文件块的请求数,随着集群增大,需要对此做出限制。否则可能会使本机负载过大而挂掉。
spark.reducer.maxReqSizeShuffleToMem
默认值:200M
参数说明:请求的文件大小高于此阈值时,会被强行溢写到产品上,防止一大堆并发请求把内存占满。
压缩与序列化参数
spark.broadcast.compress
默认值:true
参数说明:广播变量前是否会先进行压缩。(压缩方式:spark.io.compression.codec)
内存管理参数
spark.default.parallelism
参数说明:该参数用于设置每个 stage 的默认 task 数量。这个参数很重要,如果不设置可能会直接影响 Spark 作业性能。
参数调优建议:Spark 作业的默认 task 数量为 500~1000 个较为合适。如果这个参数没有设置,那么Spark会根据底层 HDFS 的 block 数量来设置task的数量(一个HDFS block 对应一个 task )。如果 task 数量偏少的话,设置的Executor进程有多少个,内存和CPU有多大,但是 task 只有1个或者 10 个,那么 90% 的 Executor 进程可能根本就没有 task 执行,就会造成资源的浪费。因此 Spark 官网建议的设置原则是,设置该参数为 num-executors * executor-cores 的 2~3
倍较为合适,比如 Executor 的总 CPU core 数量为 300 个,那么设置 1000 个 task 是可以充分地利用Spark集群的资源。
spark.storage.memoryFraction
默认值:0.6
参数说明:该参数用于设置 RDD 持久化数据在 Executor 内存中能占的比例,默认 Executor 60% 的内存,可以用来保存持久化的 RDD 数据。根据你选择的不同的持久化策略,如果内存不够时,可能数据就不会持久化,或者数据会写入磁盘。
参数调优建议:如果 Spark 作业中,有较多的 RDD 持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据,导致数据只能写入磁盘中,降低了性能。但是如果 Spark 作业中的 shuffle 类操作比较多,而持久化操作比较少,那么这个参数的值适当降低一些比较合适。此外,如果发现作业由于频繁的 GC 导致运行缓慢(通过spark web ui可以观察到作业的 GC耗时),意味着 task 执行用户代码的内存不够用,那么同样建议调低这个参数的值。
参考链接:
https://tech.meituan.com/2016/05/12/spark-tuning-pro.html
https://spark.apache.org/docs/2.2.0/configuration.html
https://people.apache.org/~pwendell/spark-releases/spark-2.2.0-rc4-docs/configuration.html