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

125 阅读5分钟

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

今天笔记主要分为四个部分:

shuffle概述

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

Map阶段:在单机上进行的针对一小块数据的计算过程。

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

Reduce阶段:对移动后的数据进行处理,依然时在单机上处理一小份数据。

为什么Shuffle对性能非常重要?

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

shuffle算子

repartition:改变分区

ByKey:给定一个key-value对,根据key聚合到一起计算的算子

Join:把本身没有在一起的数据根据某种条件放到一起

Distinct:特殊的ByKey

shuffle过程

写数据

Hash Shuffle 

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

优化前:

优化后:

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

sort shuffle

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

读数据

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

Shuffle过程的触发流程

Shuffle Handle的创建

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

shuffle Handle 与 Shuffle Writer的对应关系

BypassMergeShuffleWriter

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

UnsafeShuffleWriter

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

UnsafeShuffleWriter对内存的管理:

Long Array前24bit记录partition,如果partition超过2^24,Long Array就会溢出。

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

SortShuffleWriter

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

Reader实现

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

ShuffleBlockFetchIterator

  • 区分local和remote节省网络消耗
  • 防止OOM
  • maxBytesInFlight  限制数据块大小
  • maxReqsInFlight  限制请求数量
  • maxBlocksInFlightPerAddress 限制每一个地址上获取的block的数量
  • maxReqSizeShuffleToMem 限制放到Mem里面最大的请求大小
  • maxAttemptsOnNettyOOM 限制OOM次数

External Shuffle Service

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

Shuffer优化使用的技术-Zero Copy

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

Netty Zero Copy

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

常见的问题

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

Shuffle优化

  1. 避免shuffle:使用broadcast替代join
  1. 使用可以map-side预聚合的算子

  1. 参数优化
  1. 倾斜优化

影响:

  • 作业运行时间变长
  • Task OOM导致作业失败

优化方法:

  • 提高并行度(足够简单,只缓解、不根治)

  • AQE Skew Join

AQE根据shuffle文件统计数据自动检测倾斜数据,将那些倾斜的分区打散成小的子分区,然后各自进行join

\

push shuffle

为什么需要push shuffle?

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

Magnet

Magnet实现原理

  • Spark driver组件,协调整体的shuffle操作
  • map任务的shuffle writer过程完成后,增加了一个额外的操作push-merge,将数据复制一份推到远程的shuffle服务上
  • magnet shuffle service是一个强化版的ESS。将隶属于同一个shuffle partition的block,会在远程传输到magent后被merge到一个文件中
  • reduce任务magent shuffle service接收合并好的shuffle数据

  • bitmap:存储已merge的mapper id,防止重复merge
  • position offset:如果本次block没有正常merge,可以恢复到上一个block的位置
  • currentMapId:标识当前正在append的block,保证不同的mapper的block能依次append

Mangent可靠性

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

Cloud Shuffle Service思想

image.png

架构

写入流程

CCS AQE