Spark的shuffle过程

Spark的shuffle分为老版本的HashShuffle(现在已经弃用)和新版本的SortShuffle。Shuffle过程发生在宽依赖切分Stage的过程中,前一个Stage称作ShuffleMap Stage,后一个Stage称作Result Stage。

HashShuffle原理

未经优化的HashShuffle

在这里插入图片描述
1.Map Task将数据写入buffer缓冲区,待缓冲区达到阈值时开始溢写文件,文件数量取决于(等于)Reduce Task的数量。Reduce Task的数量取决于(等于)上一个Stage的最后一个RDD的分区数。
2.Reduce Task一边拉取对应分区的数据到buffer缓存中,一边进行处理,使用map方法对数据进行归并,直至所有的数据归并结束。
3.未经优化的Hash Shuffle每个map task都会为下游的每个reduce task创建一个磁盘文件。如果有50个map task,100个renduce task,那么就会产生5000个小文件,产生过多的小文件,极大的影响了shuffle write的性能。

优化后的HashShuffle

在这里插入图片描述
1.设置consolidateFiles为true,开启优化机制。
2.开始优化机制后,就不再是每个map task为下游的每个reduce task生成一个磁盘文件,而是一个executor为一个reduce task生成一个磁盘文件。
3.consolidateFiles机制允许多个map task复用同一个磁盘文件,可以在一定程度上对map task的数据进行合并,从而大幅减小磁盘文件的数量,提升shuffle write性能。

Sort Shuffle

普通运行机制

在这里插入图片描述
1.普通运行机制下,map task会先将数据写入到内存缓冲区中,当内存缓冲区中的数据达到一定阈值时,开始进行溢写。
2.溢写到磁盘之前,先按照数据的key进行排序,排序后分批次写入磁盘, 默认每批次1w条数据。
3.数据溢写到磁盘的过程中会产生多个临时磁盘文件,临时磁盘文件会进行merge归并,最终一个map task只会生成一个归并后的磁盘文件,同时还是生成一个对应的索引文件,记录数据的offset信息。
4.reduce task拉取对应分区的数据进行处理。

bypass运行机制

在这里插入图片描述
1.bypass运行机制触发需要满足两个条件:①不是聚合类型的shuffle算子;②map task的数量小于bypassMergeThreshold的值,默认是200。
2.bypass运行机制,map task数据溢写到磁盘之前不会对数据进行排序。
3.bypass运行机制,数据的溢写规则和未经优化的hashshuffle一样,每个map task都会为下游的每个reduce task生成一个文件,但是最后会进行数据的归并,合并为一个磁盘文件。
4.bypass机制的好处就是不对数据进行排序,节省了这一部分的资源开销。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SparkShuffle过程是指在数据处理过程中,将数据重新分区和排序的过程。它是Spark中非常重要的一个操作,用于将数据从一个RDD的分区传输到另一个RDD的分区。 SparkShuffle过程包括两个阶段:Map阶段和Reduce阶段。 在Map阶段,每个Executor上的任务(Task)会将输入数据根据指定的分区函数进行分区,并将分区后的数据写入磁盘上的.data文件中。同时,还会生成一个.index文件,用于记录每个分区的数据在.data文件中的位置信息。 在Reduce阶段,Spark会根据分区函数将数据重新分配到不同的Executor上的任务中。每个任务会读取自己负责的分区数据,并进行合并、排序等操作,最终生成最终结果。 SparkShuffle过程可以使用不同的策略来实现,其中包括BypassMergeSortShuffleWriter、SortShuffleWriter和UnsafeSortShuffleWriter等。 BypassMergeSortShuffleWriter是一种优化策略,它会尽量减少数据的复制和排序操作,提高Shuffle的性能。 SortShuffleWriter是一种常用的策略,它会将数据写入磁盘,并使用外部排序算法对数据进行排序。 UnsafeSortShuffleWriter是一种更高效的策略,它使用了内存进行排序,减少了磁盘IO的开销。 下面是一个示例代码,演示了SparkShuffle过程: ```scala val inputRDD = sc.parallelize(List(("apple", 1), ("banana", 2), ("apple", 3), ("banana", 4))) val shuffledRDD = inputRDD.groupByKey() val resultRDD = shuffledRDD.mapValues(_.sum()) resultRDD.collect().foreach(println) ``` 这段代码首先创建了一个输入RDD,其中包含了一些键值对数据。然后使用groupByKey()函数对数据进行分组,生成一个ShuffledRDD。最后使用mapValues()函数对每个分组进行求和操作,得到最终结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值