14,spark shuffle实现

Spark Shuffle演化历程

  • Spark 1.1 引入Sort Based Shuffle,但默认仍为Hash Based Shuffle
  • Spark 1.6 Tungsten-sort并入Sort Based Shuffle
  • Spark2.0 所有shuffle的方式全部统一到Sort Shuffle一个实现中

在这里插入图片描述
关于Spark的shuffle和Hadoop的shuffle是一致的,包括Shufflewrite, ShuffleRead。前者解决上游stage输出数据的分区问题,后者解决下游stage从上游stage获取数据,重新组织,并为后续操作提供数据的问题。

SortShuffleManager

在这里插入图片描述
在sparkEnv里面shuffleManager只有一种实现,SortShuffleManager。 在SortShuffleManager中,输入的数据基于分区id进行排序,写入到单独的mapoutput文件中,并且通过索引文件记录每个分区在文件中的位置,由于分区id是排序的,比较方便reducer的拉取。SortShuffleManager的实现如下:

  • registerShuffle
  • unregisterShuffle
  • getReader
  • getWriter

核心接口是reader和writer, reader只有一种实现BlockStoreShuffleReader, writer有三种实现UnsafeShuffleWriter,BypassMergeSortShuffleWriter, SortShuffleWriter

ShuffleWriter

UnsafeShuffleWriter

UnsafeShuffleWriter内部维护了一个外部排序的类ShuffleExternalSorter。将输出数据先在内存中通过ShuffleInMemorySorter根据分区id进行排序,排好序的数据经过序列化后输出的临时文件中的一段。并且记录每个分区段的位置。
整个过程就是不断地在 ShuffleInMemorySorter 插入数据,如果没有内存就申请内存,如果申请不到内存就 spill 到文件中,最终合并成一个 依据 partition id 全局有序 的大文件,整个排序只是分区级别的排序。
在这里插入图片描述

在这里插入图片描述

要求:没有聚合或者key排序
分区数目不能超过16777216
原始数据需要序列化器的支持(kryoSerializer)
需要Serializer支持relocation,在指定位置读取对应数据

BypassMergeSortShuffleWriter

每个输出分区维护一个文件句柄,直接将数据写入分区文件中,最后再讲所有分区文件合并成一个文件,并创建一个 index索引文件来标记不同分区的位置信息。类似于分桶的思想
在这里插入图片描述

要求:不需要map端的聚合操作,分区数目小于等于spark.shuffie.sort.bypassMergeThreshold(200)

缺点:如果有太多分区,就会有太多的临时文件,会对文件系统造成很大的压力

SortShuffleWriter

在不满足上述要求的情况下,返回的是 BaseShuffieHandle 对象,并且实例化一个SortShuffleWriter
在这里插入图片描述

在这里插入图片描述

SortShuffleWriter使用ExternalSorter进行外部排序,创建ExternalSorter对象,将全部数据插入到对象中。并且生成Shuffle数据文件和索引文件。创建MapStatus对象,将Shuffle数据文件和索引文件的信息进行传输。

ExternalSorter是整个SortShufflerWriter的核心,包含了两个存放数据的变量,PartitionedAppendOnlyMap, PartitionedPairBuffer。PartitionedPairBuffer类似于动态的数组,PartitionedAppendOnlyMap类似于动态的hashMap, 如果在map端有聚合操作就使用后者。支持按照分区或者分区key进行排序
在这里插入图片描述

整体流程
  • 根据map端是否要聚合选择不同的容器,聚合选择hashMap, 不聚合选择动态数组。
  • 数据进行分区,不同的分区创建不同的容器。
  • 数据写入到对应的容器中,hashMap可以做聚合操作。
  • 判断数据是否达到存储容量,达到就spill到磁盘中去。
  • 将内存中和spill的数据进行归并,支持排序,并且添加一个索引文件。

ShuffleReader

BlockStoreShuffleReader
  • 获取 ShuffleMapTask任务执行的状态信息, 通过 MapOutputTracker 的 getStatus 方法获 取 map任务执行的状态信息,得到所需要读取数据的位置
  • 知道Shuffle结果的位置后,根据不同的位置选择不同的读取方式,本地获取通过BlockManager, 远端获取通过netty读取。
  • 判断shuffleDependency是否定义聚合,如果需要就根据兼职调度Aggregator进行聚合,聚合完毕之后使用ExternalSorter进行外部排序,并且写入内存缓存去,达到阈值,spill到磁盘,最后进行归并。

内存的使用

Shuffle Write

在 map 端选择普通的排序方式,则会采用 ExternalSorter进行外排, 在内 存中存储数据时主要占用堆内执行空间 若在 map 端选择 Tungsten 的排序方式,则采用 ShuffleExternalSorter 直接对以序列 化形式存储的数据进行排序在内存中存储数据时可以
占用堆外或堆内执行空间,取决于用户是否开启了堆外内存,以及堆外执行内存是否足够。

Shuffle Read

在对 reduce 端的数据进行聚合时,要将数据交给 Aggregator 处理,在内存 中存储数据时占用堆内执行空间 。 如果需要进行 最终结果 排序,则要再次将数据交给 ExternalSorter处理,占用堆内执行空间

与Hadoop Shuffle机制对比

都是分为shuffle write, shuffle reade, hadoop的shuffle write过程比较明确,数据经过分区处理后输出到一个固定大小的spill buffer中,如果被填满就将spill buffer中的record按照key进行排序输出到磁盘中。这点类似于spark的动态数组PartitionedPairBuffer, 不同的是hadoop是严格按照key进行排序的,而PartitionedPairBuffer更加灵活(支持分区或者分区+key排序), 而在map端存在combine情况下,hadoop需要等待所有record都spill到磁盘后,启动专门的combine阶段,将所有spill文件的record进行全局聚合,得到最终的聚合结果,这个过程是多次的,因为每次支队某个分区的spill进行聚合。

在这里插入图片描述

参考

http://ixiaosi.art/2019/09/02/spark/spark-shuffle%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/

https://www.cnblogs.com/itboys/p/9201750.html
https://www.jianshu.com/p/ca1f59429fce

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值