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

162 阅读5分钟

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

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

一、本堂课重点内容

  1. Shuffle 是什么,为什么需要 Shuffle ,Shuffle 的基本过程是怎样的

  2. 介绍 Spark 中常用的 Shuffle 算子

  3. Spark 中 Shuffle 的核心原理

  4. Push Shuffle 各社区包括 Spark3.2 实现方案以及字节方案

二、详细知识点介绍

01. Shuffle 概述

1.1 MapReduce 概述

64.JPG 图源:教学PPT

1.1 Map 阶段

是在单机上进行的针对一小块数据的计算过程。

1.2 Shuffle 阶段

在 map 阶段的基础上,进行数据移动,为后续的 reduce 阶段做准备。

1.3 Reduce 阶段

对移动后的数据进行处理,依然是在单机上处理一小份数据

1.4 为什么 shuffle 对性能非常重要

  • M*R 次网络连接
  • 大量的数据移动
  • 数据丢失风险
  • 可能存在大量的排序操作
  • 数据压缩

01.总结

在大数据场景下,数据 shuffle 表示了不同分区数据交换的过程,不同的 shuffle 策略性能差异较大。

目前在各个引擎中 shuffle 都是优化的重点,在 spark 框架中,shuffle 是支撑 spark 进行大规模复杂数据处理的基石。

02. Shuffle 算子

2.1 Shuffle 算子分类

repartitionByKeyjoinDistinct
coalescegroupByKeycogroupdistinct
repartitionreduceByKeyjoin
aggregateByKeyleftOuterJoin
combineByKeyintersection
sortByKeysubtract
sortBysubtractByKey

2.2 Spark 中对 shuffle 的抽象

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

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

2.2.1 Shuffle Dependency 构造

  • 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.

—— Partitioner

  • 两个接口
    • numberPartitions
    • getPartition
  • 经典实现
    • HashPartitioner
abstract class Partitioner extends Serializable{
    def numPartitions:Int
    def getPartition(key:Any):Int
}

—— Aggregator

  • createCombiner:只有一个 value 的时候初始化的方法
  • mergeValue:合并一个 value 到 Aggregator 中
  • mergeCombiners:合两个 Aggregator

03. Shuffle 过程

3.1 Hash Shuffle - 写数据

每个 partition 会映射到一个独立的文件

—— 写数据优化

每个 partition 会映射到一个文件片段

3.2 Sort shuffle:写数据

每个 task 生成一个包含所有 partition 数据的文件

3.3 Shuffle - 读数据

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

3.6 Shuffle Handle 与 Shuffle Writer 的对应关系

BypassMergeSortShuffleHandle -> BypassMergeSortShuffleWriter

SerializedShuffleHandle -> UnsafeShuffleWriter

BaseShuffleHandle -> SortShuffleWriter

3.7 Writer 实现 - BypassMergeSortShuffleWriter

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

—— UnsafeShuffleWriter

  • 使用类似内存页储存序列化数据
  • 数据写入后不再反序列化
  • 只根据 partition 排序 Long Array
  • 数据不移动

—— SortShuffleWriter

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

3.8 Reader 实现 - 网络时序图

  • 使用基于 netty 的网络通信框架
  • 位置信息记录在 MapOutputTracker 中
  • 主要会发送两种类型的请求
    • OpenBlocks 请求
    • Chunk 请求或 Stream 请求

—— ShuffleBlockFetchIterator

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

—— External Shuffle Service

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

3.9 Shuffle 优化使用的技术 - Zero Copy

DMA(Direct Memory Access):直接存储器存取,是指外部设备不通过 CPU 而直接与系统内存交换数据的接口技术。

—— Netty Zero Copy

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

3.10 常见问题

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

3.11 Shuffle 优化

  • 使用可以 map-side 预聚合的算子

04. Push Shuffle

4.1 为什么需要 Push Shuffle?

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

4.2 Push Shuffle 的实现

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

4.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 的位置

  • currentMapId:标识当前正在 append 的 block,保证不同 mapper 的 block 能依次 append

4.4 Magnet 可靠性

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

4.6 Cloud Shuffle Service 架构

65.JPG

图源:教学PPT

4.7 实践案例 - CSS 优化

三、课程总结

  1. Shuffle 概述
  • 什么是 shuffle,shuffle 的基本流程
  • 为什么 shuffle 对性能影响非常重要
  1. Shuffle 算子
  • 常见的 shuffle 算子
  • 理解宽依赖和窄依赖,ShuffleDependency 及其相关组件
  1. Shuffle过程
  • Spark 中 shuffle 实现的历史
  • Spark 中主流版本的 shuffle 写入和读取过程
  1. Push shuffle
  • Magnet Push Shuffle 的设计思路
  • Cloud Shuffle Service 的设计实现思路