Spark Shuffle

触发 Shuffle 的操作

在这里插入图片描述

在 Spark 作业过程什么时候产生 Shuffle ?

    Shuffle描述着数据从map task输出到reduce task输入的这段过程。
        通常shuffle分为两部分:shuffle write:map任务写上游计算产生的中间数据。shuffle read:reduce任务读map任务产生的中间数据,用于下游计算。

    宽依赖之间会划分stage,而Stage之间就是Shuffle。在Spark的中,负责shuffle过程的执行、计算和处理的组件主要就是ShuffleManager,也即shuffle管理器。ShuffleManager随着Spark的发展有两种实现的方式,分别为HashShuffleManager和SortShuffleManager,因此spark的Shuffle有Hash Shuffle和Sort Shuffle两种。

    在Spark 1.2以前,默认的shuffle计算引擎是HashShuffleManager。HashShuffleManager 会产生大量的中间磁盘文件,进而由大量的磁盘IO操作影响了性能。因此在Spark 1.2以后的版本中,默认改成了SortShuffleManager。

    SortShuffleManager相较于HashShuffleManager的改进
        每个Task在进行shuffle操作时,虽然也会产生较多的临时磁盘文件,但是最后会将所有的临时文件合并(merge)成一个磁盘文件,因此每个Task就只有一个磁盘文件。在下一个stage的shuffle read task拉取自己的数据时,只要根据索引文件读取每个磁盘文件中的部分数据即可。

HashShuffleManager

    HashShuffleManager 的运行机制主要分成两种,一种是普通运行机制,另一种是合并的运行机制。合并机制主要是通过复用buffer来优化Shuffle过程中产生的小文件的数量。Hash shuffle是不具有排序的Shuffle。

    普通运行机制
    shuffle write阶段
        将每个task处理的数据按key进行“分区”,从而将相同key都写入同一个磁盘文件中,而每一个磁盘文件都只属于reduce端的stage的一个task。
        !!executor的每个 task 会为下一个stage的task创建对应数量的文件,在将数据写入磁盘之前,会先将数据写入内存缓冲中,当内存缓冲填满之后,才会溢写到磁盘文件中去。
    shuffle read阶段
        该stage的每一个task就需要将上一个stage的计算结果中的所有相同key,从各个节点上通过网络都拉取到自己所在的节点上,然后进行key的聚合或连接等操作。shuffle read的拉取过程是一边拉取一边进行聚合的。

    Hash shuffle普通机制的问题
        1、Shuffle前在磁盘上会产生海量的小文件,建立通信和拉取数据的次数变多,此时会产生大量耗时低效的 IO 操作 (因為产生过多的小文件)
        2、可能导致OOM,大量耗时低效的 IO 操作 ,导致写磁盘时的对象过多,读磁盘时候的对象也过多,这些对象存储在堆内存中,会导致堆内存不足,相应会导致频繁的GC,GC会导致OOM。

    合并的运行机制
        合并机制就是复用buffer,开启合并机制的配置是spark.shuffle.consolidateFiles。该参数默认值为false,将其设置为true即可开启优化机制。通常来说,如果我们使用HashShuffleManager,那么都建议开启这个选项。
        !!同一个 executor 的所有 task 共用一个 buffer。
        开启consolidate机制之后,在shuffle write过程中,磁盘文件的数量与下游stage的task数量是相同的。

    Hash shuffle合并机制的问题
        如果 Reducer 端的并行任务或者是数据分片过多的话则 Core * Reducer Task 依旧过大,也会产生很多小文件。

SortShuffleManager

    SortShuffleManager的运行机制主要分成两种,一种是普通运行机制,另一种是bypass运行机制。当shuffle read task的数量小于等于spark.shuffle.sort.bypassMergeThreshold参数的值时(默认为200),就会启用bypass机制。

    Sort shuffle的普通机制
        在该模式下,将每个task处理的数据会先写入一个内存数据结构中(默认5M),此时根据不同的shuffle算子,可能选用不同的数据结构。如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存。如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着,每写一条数据进入内存数据结构之后,就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,然后清空内存数据结构。
        在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。
        一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,然后依次写入最终的磁盘文件之中。
        因为一个task就只对应一个磁盘文件,因此还会单独写一份索引文件,其中标识了下游各个task的数据在文件中的start offset与end offset。

    Sort shuffle的bypass机制
    bypass运行机制的触发条件:
        1、shuffle map task数量小于spark.shuffle.sort.bypassMergeThreshold参数的值。
        2、不是聚合类的shuffle算子(比如reduceByKey)。
    此时task会为每个reduce端的task都创建一个临时磁盘文件,并将数据按key进行hash然后根据key的hash值,将key写入对应的磁盘文件之中。当然,写入磁盘文件时也是先写入内存缓冲,缓冲写满之后再溢写到磁盘文件的。最后,同样会将所有临时磁盘文件都合并成一个磁盘文件,并创建一个单独的索引文件。

    SortShuffleManager普通运行机制与bypass运行机制的主要区别
        普通机制在内存数据结构(默认为5M)完成排序。SortShuffle的bypass机制不会进行排序。

HashShuffleManager 创建的磁盘文件数受下一个stage的task数量
    普通机制的Hash shuffle,每个Executor创建的磁盘文件的数量的计算公式为:当前 Executo 的task数量 * 下一个stage的task数量。
    合并机制的Hash shuffle,每个Executor创建的磁盘文件的数量的计算公式为:当前 Executor CPU core的数量 * 下一个stage的task数量。

SortShuffleManager 创建的磁盘文件数不受下一个stage的task数量
    每个Executor创建的磁盘文件的数为其task数(多了个索引文件,标识了下游各个task的数据在文件中的start offset与end offset)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值