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

384 阅读7分钟

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

一、shuffle概述

1.MapReduce概述

·在开源实现的MapReduce中,存在Map、Shuffle、Reduce三个阶段。

image.png

Map阶段,是在单机上进行的针对一小块数据的计算过程,就是按照给定的方法进行筛选分类;

Shuffle 阶段,在map阶段的基础上,再把每个map处理好的数据再做移动,把相同颜色的数据放在一起,为后续的reduce阶段做准备,也就是说,map阶段将几个小块数据分类完成后,shuffle将同类型的数据进行合并;

Reduce阶段,对移动后的数据进行处理,依然是在单机上处理一小份数据,最终可以Shuffle得到的合并后的数据进行count,得到不同颜色的sum值。

2.Shuffle对性能的影响

  • M*R次网络连接:每一个reduce都要访问所有的map来获取对应的数据,同样还有等次的网络请求
  • 大量的数据移动:M*R次数据移动
  • 数据丢失风险:在移动和计算的过程中,存在丢失的风险,一旦丢失,面临着数据的重算
  • 可能存在大量的排序操作:需要在map阶段把不同的颜色数据区分开,放到不同位置需要对颜色进行排序
  • 大量的数据序列化、反序列化操作:消耗大量cpu
  • 数据压缩与解压缩:在存储大量数据过程中,压缩与解压缩也会占用大量CPU

3.总结

image.png

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

二、shuffle算子

1.shuffle算子分类

Spark中会产生shuffle的算子大概可以分为四类

image.png

  • repartition:把数据改变分区
  • ByKey:给定k-v对,把key聚合到一起进行计算的算子
  • join:把本身没有在一起的数据按照某种条件放在一起进行计算的算子
  • distinct:特殊的ByKey操作

2.Spark中对shuffle的抽象-宽依赖、窄依赖

image.png

窄依赖:  父RDD的每个分片至多被子RDD中的一个分片所依赖。出现宽依赖会出现map stage和reduce stage,两个stage中间就会出现shuffle操作 宽依赖:  父RDD中的分片可能被子RDD中的多个分片所依赖

算子内部的依赖关系

Shuffle Dependency

  • 宽依赖对应的对象是shuffle dependency,被两个RDD所创建,CoGroupedRDD和ShuffledRDD,创建会产生shuffle的RDD时,RDD会创建Shuffle Dependency来描述Shuffle相关的信息

shuffle Dependency构造 - Partitioner

  • Partitioner
    • 负责把key映射成一个数字,这个数字代表了某一个具体的分区。用来将record映射到具体的partition的方法
    • 接口
      • numberPartitions:一共有多少个分区
      • getPartition:给一个key,说明具体的分区是哪个
    • 经典实现
      • HashPatitioner

image.png

三、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退出历史舞台 简单分为两个阶段,Hash Shuffle和Sort Shuffle。

Hash Shuffle-写数据

每个partition会映射到一个独立的文件,最终生成m*r个文件,对文件系统造成的压力大。

image.png

Hash Shuffle-写数据优化

每个partition会映射到一个文件片段,最终得到c*r个文件,c是cpu的核数,打开的文件依然是r个,依然可能面临OOM的问题。 image.png

Sort shuffle:写数据

不在给每个partition一个buffer了,所有的数据写到一个buffer里,当内存满了,通过排序的方式,把相同的数据放在一起,所以叫sort shuffle,排序会消耗更多cpu,好处是每个task会创建一个包含所有partition数据的文件,还有一个index索引文件。 image.png

Shuffle-读数据

每个reduce task分别获取所有map task生成的属于自己的片段

image.png

4.shuffle过程的触发流程

image.png

image.png

Spark源码解读(6)——Shuffle过程_scalahome的博客-CSDN博客 shuffle过程详解_follweme888的博客-CSDN博客_shuffle过程

5.Shuffle Handle的创建

Register Shuffle时做的最重要的事情时根据不同条件创建不同的shuffle Handle

image.png

6.Shuffle Handle与Shuffle Writer的对应关系

17.jpg

7.Writer实现

Writer实现-BypassMerge Shuffle Writer image.png

  • 不需要排序,节省时间
  • 写操作的时候会打开大量文件
  • 类似于Hash Shuffle

Writer实现-UnsafeShuffleWriter

image.png

  • 使用类似内存页储存序列化数据
  • 数据写入后不再反序列化

image.png 对于堆外内存的管理,record序列化往对外内存写的时候,还会再堆内有一个long Array记录record属于哪个partition,写在了内存页d额第几页。Spill会触发排序,只发生在array上,不会排record。

  • 只根据partition排序Long Array
  • 数据不移动

Writer实现-SortShuffleWriter

image.png

  • 支持combine
  • 需要combine时,使用PartitionedAppendOnlyMap,本质是个HashTable
  • 不需要combine时PartitionedPairBuffer本质是个array

【spark】关于spark的shuffle模式的一些见解_lsr40的博客-CSDN博客_shuffle形式有几种

8.Reader实现

网络时序图

image.png

  • 基于netty的网络通信框架
  • 位置信息记录在MapOutputTracker中
  • 主要发送OpenBlocks请求和Chunk或Stream请求

ShuffleBlockFetchIterator

image.png

  • 区分local和remote节省网络资源
  • 防止OOM

External Shuffle Service

image.png

  • ESS作为一个存在于每个节点上的agent,为所有Shuffle Reader提供服务,从而优化了Spark作业的资源利用率,MapTask在运行结束后可以正常退出

四、Push Shuffle

为什么需要Push Shuffle ?

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

Push Shuffle的实现

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

[SPARK][CORE] 面试问题之 3.2新的特性Push-based Shuffle源码解析 - 腾讯云开发者社区-腾讯云 (tencent.com)

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

Magnet可靠性

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