SparkShuffle

什么是Spark Shuffle?

  • reduceByKey的含义?
    • reduceByKey会将上一个RDD中的每一个key对应的所有value聚合成一个value,然后生成一个新的RDD,元素类型是<key,value>对的形式,这样每一个key对应一个聚合起来的value
  • 问题
    • 每一个key对应的value不一定都是在一个partition中,也不太可能在同一个节点上,因为RDD是分布式的弹性的数据集,他的partition极有可能分布在各个节点上
  • 如何让聚合?
    • Shuffle Write:上一个stage的每个map task就必须保证将自己处理的当前分区中的数据相同的key写入一个分区中,可能会写入多个不同的分区文件中
    • Shuffle Read:reduce task就会从上一个stage的所有task所在的机器上寻找属于自己的那些分区文件,这样就可以保证每一个key所对应的value都会汇聚到同一个节点上去处理和聚合

Spark Shuffle的迭代历史

  • ShuffleManager随着Spark的发展有两种实现的方式:分别是:HashShuffleManager(spark1.2之前使用)和SortShuffleManager,因此spark的Shuffle有Hash ShuffleSort Shuffle两种

  • 两者的比较:在Spark1.2之前,默认的Shuffle计算引擎就是HashShuffleManager,但是HashShuffleManager有着一个非常严重的弊端,就是会产生大量的中间磁盘文件,导致会产生大量的IO操作,从而影响计算的性能。因此,在Spark1.2以后,默认的Shuffle计算引擎就改成了SortShuffleManager,该方式相较于HashShuffleManager来说,有了一定的改进。主要就在于,每个Task在进行Shuffle操作时,虽然也会产生大量的临时磁盘文件,但是最后会将所有的临时磁盘文件合并成一个,因此每个Task就对应只有一个磁盘文件。在下一个stage的Shuffle read task拉取自己的数据时,只要根据索引来读取每个磁盘文件中所对应的部分数据即可

Spark Shuffle的四种策略

1.最初始的HashShuffleManager

  • 工作机制:对key执行hash算法,从而将相同的key都写入到一个磁盘文件中,而每一个磁盘文件都只属于下游stage的一个task。在将数据写入磁盘之前,会先将数据写入到内存缓冲,当内存缓冲填满之后,才会溢写到磁盘文件

  • 不足下游有几个task,上游的每一个task就要创建几个临时文件,每个文件中只存储key取hash之后相同的数据,导致当下游task的任务过多时,上游会堆积大量的小文件,从而影响性能(总结一下就是:一对多的关系)

2.经过优化的HashShuffleManager

引入了consolidate机制(文件合并机制)

  • 工作机制:在Shuffle Write过程中,上游task就不会为下游stage的每个task创建一个磁盘文件了。此时会出现ShuffleFileGroup的概念,每个shuffleFileGroup会对应一批磁盘文件,磁盘文件的数量与下游stage的task数量相同。一个Executor上有多少个CPU core,就可以并行执行多少个task。而第一批并行执行的每个task都会创建一个shuffleFileGroup,并将数据写入对应的磁盘文件中。当Executor的CPU core执行完一批task,接着执行下一批task时,下一批task就会复用之前已有的shuffleFileGroup,包括其中的磁盘文件。也就是说:此时task会将数据写入已有的磁盘文件中,而不是写入新的磁盘文件中。因此,consolidate机制允许不同的task复用同一批磁盘文件,这样就可以有效地将多个task的磁盘文件进行一定程度上的合并,从而大幅度减少磁盘文件的数量,进而提升Shuffle Write的性能

  • 触发consolidateFiles的条件

    • 需要将spark.shuffle.consolidateFiles调整为true(默认实际开启的)

总结

未经优化:
上游的task数量:m
下游的task数量:n
上游的executor数量:k  (m>=k)
总共的磁盘文件:m*n

优化之后的:
上游的task数量:m
下游的task数量:n
上游的executor数量:k  (m>=k)
总共的磁盘文件:k*n

3.SortShuffleManager普通机制

  • 工作机制:在普通模式下,数据先写入一个内存数据结构中,此时根据不同的shuffle算子,可以选用不容的数据结构:如果是由聚合操作的shuffle算子,就是用map的数据结构(边聚合边写入内存),如果是join的算子,就使用array的数据结构(直接写入内存)。接着,每写一条数据进入内存数据结构之后,就会判断是否达到了某个临界值,如果达到了临界值的话,就会尝试的将内存数据结构中的数据溢写到磁盘,然后清空内存数据结构

    在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序,排序之后,会分批将数据写入磁盘文件,默认的batch数量是10000条,也就是说,排序好的数据,会以每批次1万条数据的形式分批写入磁盘文件,写入磁盘文件是通过Java的BufferedOutputStream实现的。BufferedOutputStream是Java的缓冲输出流,首先会将数据缓冲在内存中,当内存缓冲满溢之后再一次性写入磁盘文件中。这样可以减少磁盘IO次数,提升性能

    此时task将所有数据写入内存数据结构的过程,会发送多次磁盘溢写,产生多个临时文件,最后会将之前所有的临时文件都进行合并,最后会合并成为一个大文件。最终只剩下两个文件,一个是合并之后的数据文件,一个是索引文件(标识了下游各个task的数据再文件中的start offset与end offset)。最终由下游的task根据索引文件读取相应的数据文件

4.SortShuffleManager-bypass机制(默认)

  • 工作机制:此时task会为每个下游task都创建一个临时磁盘文件,并将数据按key进行hash,然后根据key的hash值,将key写入对应的磁盘文件中。当然,写入磁盘文件时,也是先写入内存换成,缓冲写满之后再溢写到磁盘文件。最后将所有临时磁盘文件合并成一个磁盘文件,并创建一个单独的索引文件

  • bypass机制与普通机制的不同

    • 磁盘写机制不同
    • 不会进行排序

    也就是说,启用该机制最大好处在于:ShuffleWrite过程中,不需要进行数据的排序操作,也就节省掉了这部分的性能开销

  • 触发bypass机制的条件

    • shuffle map task的数量小于spark.shuffle.sort.bypassMergeThreshold参数的值(默认200)或者不是聚合类的shuffle算子(比如groupByKey)

SparkShuffle调优

主要是调整缓冲的大小,拉取次数,重试次数与等待时间,内存比例分配,是否继续排序操作等等

1.spark.shuffle.file.buffer

  • 参数说明:该参数用于设置shuffle write taskBufferedOutputStream的buffer缓冲大小(默认是32K)。将数据写入磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘
  • 调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64K),从而减少shuffle write过程中溢写磁盘文件的次数,也就可以减少磁盘IO次数,进而提高性能。在时间中发现,合理调节该参数,性能会由1%~5%的提升

2.spark.reducer.maxSizeInFlight

  • 参数说明:该参数用于设置shuffle read taskbuffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据
  • 调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如96m),从而减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升

3.spark.shuffle.io.maxRetries and spark.shuffle.io.retryWait

  • spark.shuffle.io.maxRetries
    • 参数说明:shuffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。(默认是3次
  • spark.shuffle.io.retryWait
    • 参数说明:该参数表示每次重试拉取数据的等待间隔(默认为5s
    • 调优建议:一般的调优都是将重试次数调高,不调整时间间隔

4.spark.shuffle.memoryFraction

  • 参数说明:该参数表示Executor内存中,分配给shuffle read task进行聚合操作的内存比例

5.spark.shuffle.manager

  • 参数说明:设置ShuffleManager的类型(默认sort)
  • Spark1.5之后有三个可选项
    • Hash
    • Sort
    • tungsten-sort

6.spark.shuffle.sort.bypassMergeThreshold

  • 参数说明:当ShuffleManager为SortShuffleManager时,如果shuffle read task的数量小于这个阈值(默认200),则shuffle write过程中不会进行排序操作
  • 调优建议:当你使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大些

7.spark.shuffle.consolidateFiles

  • 参数说明:如果使用HashShuffleManager,该参数有效,如果设置为true,那么就会开启consolidate机制,也就是开启优化后的HashShuffleManager,会对shuffleFileGroup进行复用
  • 调优建议:如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。在实践中尝试过,发现其性能比卡起了bypass机制的SortShuffleManager要高出10%~30%

ark.shffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。在实践中尝试过,发现其性能比卡起了bypass机制的SortShuffleManager要高出10%~30%

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值