Spark2.x优化:Shuffle相关参数优化

一、概述


     我们已经将Spark的Shuffle原理、ShuffleWrite、ShufffleRead的具体实现细节也做了深入的源码剖析, 大多数Spark作业的性能主要就是消耗在了shuffle环节,因为该环节包含了大量的磁盘IO、序列化、网络数据传输等操作。了解相关的原理之后,就可以对Spark任务的Shuffle过程进行相关的优化,之前做源码剖析的时候,涉及到的比较重要的参数也提到过,这里也结合网络上的一些调优建议,把整个Shuffle过程常用的优化参数进行了整理。注意这里是基于Spark2.x版本的优化。



二、相关参数及优化建议


1.spark.shuffle.file.buffer


默认值:

    32KB

参数说明:

   该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲区中,待缓冲区写满之后,才会溢写到磁盘。

调优建议:

    如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64KB),从而减少shuffle write过程中溢写磁盘文件的次数,也就可以减少磁盘IO次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。


2.spark.reducer.maxSizeInFlight


默认值:

    48MB

参数说明:

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

调优建议:

    如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如96MB),从而减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。


常见问题:shuffle read task阶段出现OOM

  shuffle read task去拉取shuffle writer task输出的数据,它是一边拉取一边进行聚合,shuffle read task有一块聚合内存,大小为executor memory * spark.shuffle.memoryFraction。

解决办法:

  a.增加reduce 聚合的内存的比例,设置spark.shuffle.memoryFraction 0.3

  b.增加executor memory的大小  --executor-memory 4G

  c.减少reduce task每次拉取的数据量  设置spark.reducer.maxSizeInFlight  24MB

    

3.spark.shuffle.io.maxRetyies


默认值:

    3次

参数说明:

    shuffle read task访问shuffle write task的重试次数,如果超过该次数,read端的task就会被kill掉。

调优建议:

    调大该数值,提高重试次数,防止write端在进行full gc或其他操作read被kill影响整个程序的正常执行


4.spark.shuffle.io.retryWait


默认值:

    5s

参数说明:

    shuffle read task访问shuffle write task数据的等待时间间隔

调优建议:

    调大改参数,效果同参数3


5.spark.shuffle.memoryFraction


默认值:

    0.2

参数说明:

    该参数代表了Executor内存中,分配给shuffle read task进行聚合操作的内存比例,默认是20%。

调优建议:

    如果聚合内存充足,而且很少使用持久化操作建议调高这个比例,给shuffle read的聚合操作更多内存,以避免由于内存不足导致聚合过程中频繁读写磁盘。在实践中发现,合理调节该参数可以将性能提升10%左右。


6.spark.shuffle.sort.bypassMergeThreshold


默认值:

    200

参数说明:

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

调优建议:

    当你使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大一些,大于shuffle read task的数量。那么此时就会自动启用bypass机制,map-side就不会进行排序了,减少了排序的性能开销。但是这种方式下,依然会产生大量的磁盘文件,因此shuffle write性能有待提高


7.spark.shuffle.consolidateFiles(spark2.x不需要,只spark1.x需要)


默认值:

    false

参数说明:

    如果使用HashShuffleManager,该参数有效。如果设置为true,那么就会开启consolidate机制,会大幅度合并shuffle write的输出文件,对于shuffle read task数量特别多的情况下,这种方法可以极大地减少磁盘IO开销,提升性能。

调优建议:

    如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。在实践中尝试过,发现其性能比开启了bypass机制的SortShuffleManager要高出10%~30%。


8.spark.shuffle.compress和 spark.shuffle.spill.compress


默认值

    这两个参数的默认配置都是true。

参数说明:

    spark.shuffle.compress和spark.shuffle.spill.compress都是用来设置Shuffle过程中是否对Shuffle数据进行压缩;其中前者针对最终写入本地文件系统的输出文件,后者针对在处理过程需要spill到外部存储的中间数据,后者针对最终的shuffle输出文件。


调优建议:

    对于参数spark.shuffle.compress,如果下游的Task通过网络获取上游Shuffle Map Task的结果的网络IO成为瓶颈,那么就需要考虑将它设置为true:通过压缩数据来减少网络IO。由于上游Shuffle Map Task和下游的Task现阶段是不会并行处理的,即上游Shuffle Map Task处理完成,然后下游的Task才会开始执行。因此如果需要压缩的时间消耗就是Shuffle MapTask压缩数据的时间 + 网络传输的时间 + 下游Task解压的时间;而不需要压缩的时间消耗仅仅是网络传输的时间。因此需要评估压缩解压时间带来的时间消耗和因为数据压缩带来的时间节省。如果网络成为瓶颈,比如集群普遍使用的是千兆网络,那么可能将这个选项设置为true是合理的;如果计算是CPU密集型的,那么可能将这个选项设置为false才更好。


    对于参数spark.shuffle.spill.compress,如果设置为true,代表处理的中间结果在spill到本地硬盘时都会进行压缩,在将中间结果取回进行merge的时候,要进行解压。因此要综合考虑CPU由于引入压缩解压的消耗时间和Disk IO因为压缩带来的节省时间的比较。在Disk IO成为瓶颈的场景下,这个被设置为true可能比较合适;如果本地硬盘是SSD,那么这个设置为false可能比较合适。


9.spark.shuffle.service.enabled


默认值

     false

参数说明:

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

优化建议:

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


猜你喜欢

转载自blog.51cto.com/15080019/2653877