Spark Shuffle实现

整理记录一下Spark Shuffle的实现,大部分摘自Spark Shuffle原理及相关调优,可直接移步前往学习。

概述

Spark是基于MapReduce思想实现的计算框架。

在MapReduce中,通过shuffle来连接map和reduce,将map输出作为reduce的输入。

Spark作为MR的实现,其中也存在shuffle流程。

Spark根据RDD的宽依赖划分stage,stage中又包含了task。每个stage中的task依赖上游stage中task的输出,上游task落盘称为shuffle写,下游task读称为shuffle读上游task相当于MR的map阶段,下游task相当于MR的reduce阶段。不同stage间task的读写构成了spark的shuffle流程。

下游task(reduce端)会去上游task(map端)所在节点读取自己需要的分区数据。整个过程涉及到序列化、磁盘IO等操作。

Spark 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 Shuffle
  • Spark 1.2 默认的Shuffle方式改为Sort Based Shuffle
  • Spark 1.4 引入Tungsten-Sort Based Shuffle
  • Spark 1.6 Tungsten-sort并入Sort Based Shuffle
  • Spark 2.0 Hash Based Shuffle退出历史舞台

Spark Shuffle的实现

Spark现在(2.x +)默认shuffle写的实现方式是Sort Shuffle。源码中Hash Shuffle的相关代码已经没有了。

这里简述一下spark有史以来shuffle写的实现。

Hash Shuffle v1

在这里插入图片描述

在map阶段(shuffle写),每个map都会为下游stage的每个partition写一个临时文件。上游有M个task,下游有N个partition,就会在executor上生成M*N个文件。另一方面,如果一个executor上有K个core,那么executor同时可运行K个task,这样一来,就会同时申请K*N 个文件描述符,一旦partition数较多,势必会耗尽executor上的文件描述符,同时生成K*N个write handler也会带来大量内存的消耗。

在reduce阶段(shuffle read),每个reduce task都会拉取所有map对应的那部分partition数据,那么executor会打开所有临时文件准备网络传输,这里又涉及到大量文件描述符,另外,如果reduce阶段有combiner操作,那么它会把网络中拉到的数据保存在一个HashMap中进行合并操作,如果数据量较大,很容易引发OOM操作。

缺点:创建太多的文件;打开太多的FD;容易引发OOM。

Hash Shuffle v2

在这里插入图片描述

为了解决v1生成过多文件的问题,v2将一个executor中的所有map task的相同分区文件合并,每个executor上最多只生成N个分区文件。

缺点:只解决了v1的文件个数问题,其他问题都还在

Sort Shuffle v1

针对Hash Shuffle的弊端,在spark 1.1.0版本中引入Sort Shuffle,它参考了Hadoop MapReduce中的shuffle实现,对记录进行排序来做shuffle,如下图所示:

在这里插入图片描述

在map阶段(shuffle write),会按照partition id以及key对记录进行排序,将所有partition的数据写在同一个文件中,该文件中的记录首先是按照partition id排序一个一个分区的顺序排列,每个partition内部是按照key进行排序存放,map task运行期间会顺序写每个partition的数据,并通过一个索引文件记录每个partition的大小和偏移量。这样一来,每个map task一次只开两个文件描述符,一个写数据,一个写索引,大大减轻了Hash Shuffle大量文件描述符的问题,即使一个executor有K个core,那么最多一次性开K*2个文件描述符。

在reduce阶段(shuffle read),reduce task拉取数据做combine时不再是采用HashMap,而是采用ExternalAppendOnlyMap,该数据结构在做combine时,如果内存不足,会刷写磁盘,很大程度的保证了鲁棒性,避免大数据情况下的OOM。

特点:总体上看来Sort Shuffle解决了Hash Shuffle的所有弊端,但是因为需要其shuffle过程需要对记录进行排序,所以在性能上有所损失。

Unsafe Shuffle

从spark 1.5.0开始,spark开始了钨丝计划(Tungsten),目的是优化内存和CPU的使用,进一步提升spark的性能。为此,引入Unsafe Shuffle,它的做法是将数据记录用二进制的方式存储,直接在序列化的二进制数据上sort而不是在java 对象上,这样一方面可以减少memory的使用和GC的开销,另一方面避免shuffle过程中频繁的序列化以及反序列化。在排序过程中,它提供cache-efficient sorter,使用一个8 bytes的指针,把排序转化成了一个指针数组的排序,极大的优化了排序性能。

特点:使用Unsafe Shuffle有几个限制,shuffle阶段不能有aggregate操作,分区数不能超过一定大小(224−1224−1,这是可编码的最大partition id),所以像reduceByKey这类有aggregate操作的算子是不能使用Unsafe Shuffle,它会退化采用Sort Shuffle。

Sort Shuffle v2

从spark-1.6.0开始,把Sort Shuffle和Unsafe Shuffle全部统一到Sort Shuffle中,如果检测到满足Unsafe Shuffle条件会自动采用Unsafe Shuffle,否则采用Sort Shuffle。从spark-2.0.0开始,spark把Hash Shuffle移除,可以说目前spark-2.0中只有一种Shuffle,即为Sort Shuffle。

Spark Shuffle相关调优

参考

http://sharkdtu.com/posts/spark-shuffle.html

https://spark.apache.org/docs/latest/configuration.html#shuffle-behavior

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值