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

134 阅读3分钟

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

Shuffle 一般被翻译为数据混洗,是类 MapReduce 分布式计算框架独有的机制,也是这类分布式计算框架最重要的执行机制。Shuffle主要分为两个层面:

逻辑层面
物理层面。

逻辑层面主要从 RDD 的血统机制出发,从 DAG 的角度来分析 Shuffle,Spark 容错机制,而物理层面是从执行角度来分析 Shuffle 是如何发生的。

RDD 血统与 Spark 容错

在 DAG 中,最初的 RDD 被称为基础 RDD,后续生成的 RDD 都是由算子以及依赖关系生成的,也就是说,无论哪个 RDD 出现问题,都可以由这种依赖关系重新计算而成。这种依赖关系被称为 RDD 血统(lineage)。血统的表现形式主要分为宽依赖(wide dependency)与窄依赖(narrow dependency)

image.png

窄依赖的定义是:子 RDD 中的分区与父 RDD 中的分区只存在一对一的映射关系,而宽依赖则是子 RDD 中的分区与父 RDD 中的分区存在一对多的映射关系,那么从这个角度来说,map、 filter、 union 等就是窄依赖,而 groupByKey、 coGroup 就是典型的宽依赖

image.png

image.png

宽依赖还有个名字,叫 Shuffle 依赖,也就是说宽依赖必然会发生 Shuffle 操作,在前面也提到过 Shuffle 也是划分 Stage 的依据。而窄依赖由于不需要发生 Shuffle,所有计算都是在分区所在节点完成,它类似于 MapReduce 中的 ChainMapper。所以说,在你自己的 DAG 中,如果你选取的算子形成了宽依赖,那么就一定会触发 Shuffle。

当 RDD 中的某个分区出现故障,那么只需要按照这种依赖关系重新计算即可,窄依赖最简单,只涉及某个节点内的计算,而宽依赖,则会按照依赖关系由父分区计算而得到。

spark 的容错主要分为资源管理平台的容错和 Spark 应用的容错, Spark 应用是基于资源管理平台运行,所以资源管理平台的容错也是 Spark 容错的一部分,如 YARN 的 ResourceManager HA 机制。在 Spark 应用执行的过程中,可能会遇到以下几种失败的情况:

Driver 报错;
Executor 报错;
Task 执行失败。

Driver 执行失败是 Spark 应用最严重的一种情况,标志整个作业彻底执行失败,需要开发人员手动重启 Driver;Executor 报错通常是因为 Executor 所在的机器故障导致,这时 Driver 会将执行失败的 Task 调度到另一个 Executor 继续执行,重新执行的 Task 会根据 RDD 的依赖关系继续计算,并将报错的 Executor 从可用 Executor 的列表中去掉;Spark 会对执行失败的 Task 进行重试,重试 3 次后若仍然失败会导致整个作业失败。在这个过程中,Task 的数据恢复和重新执行都用到了 RDD 的血统机制。

Spark Shuffle

很多算子都会引起 RDD 中的数据进行重分区,新的分区被创建,旧的分区被合并或者被打碎,在重分区的过程中,如果数据发生了跨节点移动,就被称为 Shuffle,在 Spark 中, Shuffle 负责将 Map 端(这里的 Map 端可以理解为宽依赖的左侧)的处理的中间结果传输到 Reduce 端供 Reduce 端聚合(这里的 Reduce 端可以理解为宽依赖的右侧),它是 MapReduce 类型计算框架中最重要的概念,同时也是很消耗性能的步骤。Shuffle 体现了从函数式编程接口到分布式计算框架的实现。与 MapReduce 的 Sort-based Shuffle 不同,Spark 对 Shuffle 的实现方式有两种:Hash Shuffle 与 Sort-based Shuffle,这其实是一个优化的过程。在较老的版本中,Spark Shuffle 的方式可以通过 spark.shuffle.manager 配置项进行配置,而在最新的 Spark 版本中,已经去掉了该配置,统一称为 Sort-based Shuffle。