大数据 Shuffle 原理与实践 | 青训营笔记

86 阅读4分钟

大数据 Shuffle 原理与实践

这是我参与「 第四届青训营 」笔记创作活动的的第六天

1.MapReduce概述

MapReduce是一个软件框架,基于该框架能够容易地编写应用程序,这些应用程序能够运行在由上千个商用机器组成的大集群上,并以一种可靠的,具有容错能力的方式并行地处理上TB级别的海量数据集。

1.MapReduce的三个阶段

image.png Map阶段

  • 是在单机上进行的针对一小块数据的计算过程,按照给定的方式进行划分,并行处理 Shuffle 阶段
  • 在map阶段的基础上,进行数据移动,为后续的reduce阶段做准备,进行分区,合并,排序操作 Reduce阶段
  • 对移动后的数据进行处理,依然是在单机上处理一小份数据,汇总整理的阶段

2.shuffle重要的原因

数据shuffle表示了不同分区数据交换的过程,不同的shuffle策略性能差异较大。目前在各个引擎中shuffle都是优化的重点,在spark框架中,shuffle是支撑spark进行大规模复杂数据处理的基石。

2.shuffle算子

1.shuffle算子分类

image.png

2.宽依赖与窄依赖

窄依赖:父RDD的每个分片至多被子RDD中的一个分片所依赖

宽依赖:父RDD中的分片可能被子RDD中的多个分片所依赖

image.png

  • 创建会产生shuffle的RDD时,RDD会创建Shuffle Dependency来描述Shuffle相关的信息
  • 构造函数
    • A single key-value pair RDD, i.e. RDD[Product2[K, V]],
    • Partitioner (available as partitioner property),
    • Serializer,
    • Optional key ordering (of Scala’s scala.math.Ordering type),
    • Optional Aggregator,
    • mapSideCombine flag which is disabled (i.e. false) by default.

3.Partitioner

  • 用来将record映射到具体的partition的方法
  • 接口
    • numberPartitions
    • getPartition

4.Aggregator

  • 在map侧合并部分record的函数
  • 接口
    • createCombiner:只有一个value的时候初始化的方法
    • mergeValue:合并一个value到Aggregator中
    • mergeCombiners:合并两个Aggregator

3.shuffle过程

1.Shuffle实现的发展历程

  • Spark 0.8及以前Hash Based Shuffle
  • Spark 0.8.1 为Hash Based Shuffle引入File Consolidation机制
  • Spark 0.9 引入ExternalAppendOnlyMap
  • Spark 1.1 引入Sort Based Shuffle,但默认仍为Hash Based Shufle
  • Spark 1.2 默认的Shuffle方式改为Sort Based Shuffle
  • Spark 1.4 引入Tungsten-Sort Based Shuffle
  • Spark 1.6 Tungsten-Sort Based Shuffle并入Sort Based Shuffle
  • Spark 2.0 Hash Based Shuffle退出历史舞台

2.Spark中的shuffle变迁过程

  • HashShuffle
    • 优点:不需要排序
    • 缺点:打开,创建的文件过多
  • SortShuffle
    • 优点:打开的文件少、支持map-side combine
    • 缺点:需要排序
  • TungstenSortShuffle
    • 优点:更快的排序效率,更高的内存利用效率
    • 缺点:不支持map-side combine 三种ShuffleHandle对应了三种不同的ShuffleWriter的实现
  • BypassMergeSortShuffleWriter:HashShuffle
  • UnsafeShuffleWriter:TunstonShuffle
  • SortSHuffleWriter:SortShuffle image.png

3ShuffleReader网络请求流程

image.png 使用netty作为网络框架提供网络服务,并接受reducetask的fetch请求

首先发起openBlocks请求获得streamId,然后再处理stream或者chunk请求

4.ShuffleBlockFetchIterator

  • 区分local和remote节省网络消耗
  • 防止OOM
    • maxBytesInFlight
    • maxReqsInFlight
    • maxBlocksInFlightPerAddress
    • maxReqSizeShuffleToMem
    • maxAttemptsOnNettyOOM

5.External Shuffle Service

image.png 为了解决Executor为了服务数据的fetch请求导致无法退出问题,我们在每个节点上部署一个External Shuffle Service,这样产生数据的Executor在不需要继续处理任务时,可以随意退出。

6.Shuffle优化使用的技术: Netty Zero Copy

  • 可堆外内存,避免JVM堆内存到堆外内存的数据拷贝。
  • CompositeByteBuf 、Unpooled. wrappedBuffer、ByteBuf.slice ,可以合并、包装、切分数组,避免发生内存拷贝
  • Netty 使用FileRegion实现文件传输,FileRegion 底层封装了FileChannel#transferTo() 方法,可以将文件缓冲区的数据直接传输到目标Channel,避免内核缓冲区和用户态缓冲区之间的数据拷贝

7.常见问题

  • 数据存储在本地磁盘,没有备份
  • IO并发:大量RPC请求(M*R)
  • IO吞吐:随机读、写放大(3X)
  • GC频繁,影响NodeManager

4.Push Shuffle

1.需要Push Shuffle的原因

  • Avg IO size太小,造成了大量的随机IO,严重影响磁盘的吞吐
  • M*R次读请求,造成大量的网络连接,影响稳定性

2.Push Shuffle的实现

  • Facebook:cosco
  • Linkdin:magnet
  • Uber:Zeus
  • Alibaba:RSS
  • Tencent:FireStorm
  • Bytedance:CSS
  • Spark3.2:push based shuffle

3.Magnet实现原理

  • Spark driver组件,协调整体的shuffle操作
  • map任务的shuffle writer过程完成后,增加了-个额外的操作push. merge,将数据复制一份推到远程shuffle服务上
  • magnet shuffle service是一个强化版的ESS。将隶属于同一个shuffle partition的block,会在远程传输到magnet后被merge到一个文件中
  • reduce任务从magnet shuffle service接收合并好的shuffle数据
  • bitmap:存储已merge的mapper id,防止重复merge
  • position offset:如果本次block没有正常merge,可以恢复到上一个block的位置
  • currentMapld:标识当前正在append的block,保证不同mapper 的block能依次append

4.Magnet可靠性

  • 如果Map task输出的Bolck没有成功Push到magnet上,并且反复重试仍然失败,则reduce task直接从ESS上拉取原始block数据
  • 如果magnet上的block因为重复或者冲突等原因,没有正常完成merage的过程,则reduce task直接拉取未完成merge的block
  • 如果reduce拉取已经merge好的block失败,则会直接拉取merge前的原始block
  • 本质上,magnet中维护了两份shuffle数据的副本