Spark之Shuffle参数调优解析

在分布式系统中,数据分布在不同的节点上,每个节点计算一部分数据,后续将各个节点的数据进行汇聚,此时会出现shuffle,shuffle会产生大量的磁盘IO,网络IO,压缩,解压缩,序列化,反序列化等操作,这系列操作对性能都是很大的负担。

下面是spark2.2.0版本的shuffle的属性表,http://spark.apache.org/docs/2.2.0/configuration.html

一、Shuffle 参数

Property NameDefaultMeaning中文释义
spark.reducer.maxSizeInFlight48mMaximum size of map outputs to fetch simultaneously from each reduce task. Since each output requires us to create a buffer to receive it, this represents a fixed memory overhead per reduce task, so keep it small unless you have a large amount of memory.从每个reduce任务同时抓取map输出数据的最大大小,由于每个输出数据需要创建一个缓冲区来接受,是每个reduce任务固定的内存开销,因此需要设置一个较小的值,除非有大量的内存
spark.reducer.maxReqsInFlightInt.MaxValueThis configuration limits the number of remote requests to fetch blocks at any given point. When the number of hosts in the cluster increase, it might lead to very large number of in-bound connections to one or more nodes, causing the workers to fail under load. By allowing it to limit the number of fetch requests, this scenario can be mitigated.限制了每个主机每次进行reduce操作时可以被多少台远程主机拉取文件块,配置在任何给定节点获取块的请求远程请求数,当集群中主机数量增加时,可能会导致一个或者多个节点的入站连接数量非常多,导致负载失败,限制次参数,可以缓解这种情况
spark.shuffle.compresstrueWhether to compress map output files. Generally a good idea. Compression will use spark.io.compression.codec.是否压缩map输出文件,默认压缩 true,spark2.2之前默认压缩方式snappy,spark2.2之后默认是lz4
spark.shuffle.file.buffer32kSize of the in-memory buffer for each shuffle file output stream. These buffers reduce the number of disk seeks and system calls made in creating intermediate shuffle files.在内存输出流中 每个shuffle文件占用内存大小,适当提高 可以减少磁盘读写 io次数,初始值为32k
spark.shuffle.io.maxRetries3(Netty only) Fetches that fail due to IO-related exceptions are automatically retried if this is set to a non-zero value. This retry logic helps stabilize large shuffles in the face of long GC pauses or transient network connectivity issues.shuffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功,就可能会导致作业执行失败。
对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次),以避免由于JVM的full gc或者网络不稳定等因素导致的数据拉取失败。在实践中发现,对于针对超大数据量(数十亿~上百亿)的shuffle过程,调节该参数可以大幅度提升稳定性。
spark.shuffle.io.numConnectionsPerPeer1(Netty only) Connections between hosts are reused in order to reduce connection buildup for large clusters. For clusters with many hard disks and few hosts, this may result in insufficient concurrency to saturate all disks, and so users may consider increasing this value.机器之间的可以重用的网络连接,主要用于在大型集群中减小网络连接的建立开销,如果一个集群的机器并不多,可以考虑增加这个值
spark.shuffle.io.preferDirectBufstrue(Netty only) Off-heap buffers are used to reduce garbage collection during shuffle and cache block transfer. For environments where off-heap memory is tightly limited, users may wish to turn this off to force all allocations from Netty to be on-heap.启用堆外内存,可以避免shuffle过程的频繁gc,如果堆外内存非常紧张,则可以考虑关闭这个选项
spark.shuffle.io.retryWait5s(Netty only) How long to wait between retries of fetches. The maximum delay caused by retrying is 15 seconds by default, calculated as maxRetries * retryWait.每次重试拉取数据的等待间隔,默认是5s,建议加大时长,理由同上,保证shuffle操作的稳定性
spark.shuffle.service.enabledfalseEnables the external shuffle service. This service preserves the shuffle files written by executors so the executors can be safely removed. This must be enabled if spark.dynamicAllocation.enabled is "true". The external shuffle service must be set up in order to enable it. See dynamic allocation configuration and setup documentation for more information.启用外部shuffle服务,这个服务会安全地保存shuffle过程中,executor写的磁盘文件,因此executor即使挂掉也不要紧,必须配合spark.dynamicAllocation.enabled属性设置为true,才能生效,而且外部shuffle服务必须进行安装和启动,才能启用这个属性
spark.shuffle.service.port7337Port on which the external shuffle service will run.外部shuffle服务的端口号,具体解释同上
spark.shuffle.service.index.cache.entries1024Max number of entries to keep in the index cache of the shuffle service.在shuffle服务的索引缓存中最大条目数
spark.shuffle.sort.bypassMergeThreshold200(Advanced) In the sort-based shuffle manager, avoid merge-sorting data if there is no map-side aggregation and there are at most this many reduce partitions.对于sort-based ShuffleManager,如果没有进行map side聚合,而且reduce task数量少于这个值,那么就不会进行排序,如果你使用sort ShuffleManager,而且不需要排序,那么可以考虑将这个值加大,直到比你指定的所有task数量都大,以避免进行额外的sort,从而提升性能
spark.shuffle.spill.compresstrueWhether to compress data spilled during shuffles. Compression will use spark.io.compression.codec.shuffle过程中溢出的文件是否压缩,默认true,使用spark.io.compression.codec压缩。spark2.2之前默认压缩方式snappy,spark2.2之后默认是lz4
spark.shuffle.accurateBlockThreshold100 * 1024 * 1024When we compress the size of shuffle blocks in HighlyCompressedMapStatus, we will record the size accurately if it's above this config. This helps to prevent OOM by avoiding underestimating shuffle block size when fetch shuffle blocks.以字节为单位的阈值,高于该阈值可准确记录HighlyCompressedMapStatus中随机块的大小。这有助于通过避免在获取shuffle块时低估shuffle块大小来防止OOM。
spark.io.encryption.enabledfalseEnable IO encryption. Currently supported by all modes except Mesos. It's recommended that RPC encryption be enabled when using this feature.是否启用加密,目前支持除mesos外的所有模式,建议使用前先开启RPC加密
spark.io.encryption.keySizeBits128IO encryption key size in bits. Supported values are 128, 192 and 256.IO加密密钥大小,单位是位
spark.io.encryption.keygen.algorithmHmacSHA1The algorithm to use when generating the IO encryption key. The supported algorithms are described in the KeyGenerator section of the Java Cryptography Architecture Standard Algorithm Name Documentation.生成IO加密密钥时使用的算法

二、参数详解

1、spark.shuffle.manager(默认值是sort)

该参数用于设置shufflemanager的类型,spark1.5以后,

有3个选项

(1)Hash

spark1.2以前默认,spark2.0之后被废弃并移除

spark2.2.0版本下sparkEnv类中

val shortShuffleMgrNames = Map(
  "sort" -> classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName,
  "tungsten-sort" -> classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName)
val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")

sort设置为默认,hash被移除

(2)sort

spark1.2之后默认,

(3)tungsten-sort

与sort类似,tungsten-sort使用了堆外内存管理机制,内存使用效率更高,但是这种方式慎用,线上使用有时会出现bug。

调优建议

sortshufflemanager默认会对数据排序,如果业务逻辑中需要排序,则使用默认shuffle机制即可。

如果业务中不需要排序,通过bypass机制来避免排序,同时提供较好的磁盘读写性能。

2、spark.shuffle.blockTransferService 

spark.shuffle.blockTransferService 参数用来实现Executor之间传递shuffle缓存块,主要方式有netty和nio

(1)netty

spark1.2之后默认,使用netty实现更加简洁

(2)nio

spark1.2之前的默认

3、spark.shuffle.compress

该参数判断是否对mapper端聚合输出进行压缩,默认是true,表示在每个shuffle过程中都会对mapper端的输出进行压缩,减少shuffle过程中下一个stage向上一个stage抓取数据的网络开销,减轻shuffle的压力。

调优建议

压缩也是要消耗大量的CPU资源的,所以打开压缩选项会增加Map任务的执行时间,因此如果在CPU负载的影响远大于磁盘和网络带宽的影响的场合下,也可能将spark.shuffle.compress 设置为False才是最佳的方案

4、spark.io.compression.codec

压缩内部数据,如RDD分区,广播变量和shuffle输出的数据,该参数的值有三个选项,snappy,lz4和lzf

spark2.2之前默认压缩方式snappy,spark2.2之后默认是lz4

5、spark.shuffle.consolidateFiles

如果使用HashShuffleManager,该参数有效。如果设置为true,那么就会开启consolidate机制,会大幅度合并shuffle write的输出文件,对于shuffle read task数量特别多的情况下,这种方法可以极大地减少磁盘IO开销,提升性能。
如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shuffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。

注意:该参数在spark2.0之后废弃

6、spark.shuffle.file.buffer

在shuffleMapTask端也会增大map任务的写磁盘的缓存,默认32k,数据写到磁盘之前,先写入buffer缓存,待缓存写满后,再溢写到磁盘。

默认使用这么小的缓存,是希望在硬件较小的情况下也可以部署

调优建议

如果作业可用的内存比较充足,可以适当调大该参数,比如(64k),从而减少shuffer write的溢写磁盘的次数,即减少磁盘IO次数,提升性能。

7、spark.shuffle.io.maxRetries

shuffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功,就可能会导致作业执行失败。
对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次),以避免由于JVM的full gc或者网络不稳定等因素导致的数据拉取失败。

调优建议

在实践中发现,对于针对超大数据量(数十亿~上百亿)的shuffle过程,调节该参数可以大幅度提升稳定性。

默认值是3次,通常建议调节到8~10次

8、spark.shuffle.io.retryWait

每次重试拉取数据的等待间隔,默认是5s,建议加大时长,理由同上,保证shuffle操作的稳定性

9、spark.shuffle.io.numConnectionsPerPeer

机器之间的可以重用的网络连接,主要用于在大型集群中减小网络连接的建立开销,如果一个集群的机器并不多,可以考虑增加这个值

10、spark.reducer.maxSizeInFlight

设置shuffle read task的buffer大小,而这个buffer决定了每次能够拉取多少数据。

从每个reduce任务同时抓取map输出数据的最大大小,由于每个输出数据需要创建一个缓冲区来接受,是每个reduce任务固定的内存开销,因此需要设置一个较小的值,除非有大量的内存

调优建议

如果作业的内存资源充足,适当调大buffer的大小,从而减少拉取次数,网络传输次数,进而提高性能

11、spark.shuffle.io.preferDirectBufs

启用堆外内存,可以避免shuffle过程的频繁gc和缓存复制,如果堆外内存非常紧张,则可以考虑关闭这个选项

12、spark.shuffle.memoryFraction

该参数用于shuffle read task进行聚合操作的内存占比

在spark1.6之前使用静态内存管理,spark.shuffle.memoryFraction默认值是0.2

spark1.6之后使用统一内存管理,spark.memory.fraction 默认是0.75,spark2.0后修改该参数为0.6,执行内存的空间和存储内存的空间可以相互借用内存

13、spark.shuffle.service.enabled

设置客户端读取Executor上的shuffle文件的方式,默认值是false,使用BlockTransferService读取

当设置为true时,BlockManager实例生成时,需要读取spark.shuffle.service.port配置的端口,同时对应的BlockManager的shuffleclient不再是默认的BlockTransferService实例,而是ExternalShuffleClient实例。

启用外部shuffle服务,这个服务会安全地保存shuffle过程中,executor写的磁盘文件,因此executor即使挂掉也不要紧,必须配合spark.dynamicAllocation.enabled属性设置为true,才能生效,而且外部shuffle服务必须进行安装和启动,才能启用这个属性。

NodeManager中一个长期运行的辅助服务,用于提升shuffle计算性能。

Spark系统在运行含shuffle过程的应用时,Executor进程除了运行task,还要负责写shuffle数据,给其他Executor提供shuffle数据。当Executor进程任务过重,导致GC而不能为其他Executor提供shuffle数据时,会影响任务运行。 External shuffle Service是长期存在于NodeManager进程中的一个辅助服务。通过该服务来抓取shuffle数据,减少了Executor的压力,在Executor GC的时候也不会影响其他Executor的任务运行。

在NodeManager中启动External shuffle Service。在“yarn-site.xml”中添加如下配置项:

<property>   
    <name>yarn.nodemanager.aux-services</name>                 
    <value>spark_shuffle</value>   
</property>
<property>
    <name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
    <value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>
<property>
    <name>spark.shuffle.service.port</name>
    <value>7337</value>
</property>

14、spark.shuffle.sort.bypassMergeThreshold

shufflemanager为sortshufflemanager时,如果shuffle read task的数量小于这个阈值(默认200),则shuffle write过程中不会进行排序操作,而是直接按照未经优化的hashshufflemanager方式写数据,但是最后会将每个task产生的临时文件都合并为一个文件,并单独创建一个索引文件。

调优建议

当使用sortshufflemanager时,如果不需要排序,那么建议将这个参数调大,大于shuffle read task的数量。那么就会自动启动bypass机制,map-side不会排序,减少排序的开销,但是这种方式会产生大量文件,虽然最后合并了。

如果需要排序,则直接使用默认,此时不会产生大量文件再合并的操作。

本质上是对hashshufflemanager的一个折中方案,用于设置reduce端的分区数小于配置的数值时,sort shuffle 内部不使用merge sort方式处理数据,而是直接将数据写入单个分区,这个方式和hash的方式类似,区别是最后会将所有分区数据合并成一个文件,并生成一个索引文件,来标记不同的分区的位置信息。

对于reducer而言,数据文件和索引文件的格式和内部是否做过merge sort是完全相同的。

bypass机制和hashshufflemanager都有一个问题,就是会同时打开多个文件,导致内存增加的问题。

15、spark.shuffle.spill.compress和spark.shuffle.spill

shuffle过程中,如果涉及排序,聚合等操作会在内存中维护数据结构,进而占用内存。
如果内存不够,会出现两种情况,oom和spill写到磁盘。

hashshufflemanager在shuffle write操作时是全内存操作,没有spill操作,所以会出现oom。

sortshufflemanager在shuffle write操作时是有spill操作。

shuffle read过程中需要聚合和排序时,也会将数据spill写到磁盘中。

对于spark.shuffle.spill.compress而言,情况类似,但是spill数据不会被发送到网络中,仅仅是临时写入本地磁盘,而且在一个任务中同时需要执行压缩和解压缩两个步骤,所以对CPU负载的影响会更大一些,而磁盘带宽(如果标配12HDD的话)可能往往不会成为Spark应用的主要问题,所以这个参数相对而言,或许更有机会需要设置为False。

总之,Shuffle过程中数据是否应该压缩,取决于CPU/DISK/NETWORK的实际能力和负载,应该综合考虑。

 

 

 

 

 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值