Spark 3.2.0 版本新特性 push-based shuffle 论文详解(三)系统设计

前言

本文隶属于专栏《大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和参考文献请见大数据技术体系


目录

  1. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(一)概要和介绍
  2. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(二)背景和动机
  3. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(三)系统设计
  4. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(四)实现优化
  5. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(五)评估结果
  6. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(六)相关工作
  7. Spark 3.2.0 版本新特性 push-based shuffle 论文详解(七)结论

思维导图

在这里插入图片描述


系统设计

对于第 2 节中描述的问题,我们提出了 Magnet,这是 Spark 的另一种 shuffle 机制。

Magnet 旨在保持当前 Spark shuffle 操作的容错优势,该操作以基于排序的方式物化了中间 shuffle 数据,同时克服了上述问题。

在设计 Magnet 时,我们必须克服几个挑战。

首先,Magnet 需要在 shuffle 操作过程中提高磁盘 I/O 效率。

它应该避免从磁盘中读取单个小的 shuffle 块,这会损害磁盘吞吐量。

其次,Magnet 应该有助于缓解潜在的 Spark ESS 连接故障,以提高大规模 Spark 集群中 shuffle 操作的整体可靠性。

第三,Magnet 需要应对潜在的落后者和数据倾斜,这在具有实际生产工作负载的大型集群中很常见。

最后,Magnet 需要在不产生过多内存或 CPU 开销的情况下实现这些优势。

这对于使 Magnet 成为一种可扩展的解决方案,从而处理具有数千个节点和 PB 级别每天 shuffle 数据的集群来说至关重要。


3.1 解决方案概述

图 4 展示了 Magnet 的架构。

在这里插入图片描述

Magnet 的架构图,3.1 节中会详解每一步。

Spark 中四个现有组件(Spark Driver,Spark ESS,Spark Shuffle Map Task 和 Spark Shuffle Reduce Task)在我们的设计中延伸了额外的行为。

更多详细信息涵盖了后面的几个小节。

  • Spark Driver 协调 Map Task,Reduce Task 和 Shuffle Services 之间的整个 Shuffle 操作。(图 4 中的步骤 1、6、7)
  • Shuffle Map Task 现在要处理其物化 shuffle 数据的额外准备工作,以便推动到远程的 Shuffle Services。 (图 4 中的步骤 2、3)
  • Magnet Shuffle Service,这是增强的 Spark ESS,接受远程推送的 Shuffle 块,并将它们合并到每个 Shuffle 分区唯一对应的合并Shuffle 文件中。 (图 4 中的步骤 4)
  • Shuffle Reduce Task 现在利用这些已合并的 shuffle 文件,这些文件通常与任务本身共同定位,以提高获取其任务输入的效率。 (图 4 中的步骤 8)

下面简要描述Magnet的一些关键特征。

  • Push-Merge Shuffle - Magnet采用 Push-Merge Shuffle 机制,其中 Mapper 生成的 Shuffle 数据被推送到远程的 Magnet Shuffle Service,从而实现每个 shuffle 分区都能被合并。这允许Magnet将小的 Shuffle 块的随机读取转化成 MB 大小块的顺序读取。此外,此推送操作与 Mapper 分离,这样的话,如果操作失败,也不会增加 Map Task 的运行时间或者导致 Map Task 失败。
  • 最给力的方法 - Magnet不需要块 push 操作完成的那么完美。通过执行Push-Merge ShuffleMagnet有效地复制了 shuffle 数据。Magnet允许 reducer 获取合并的和未合并的 shuffle 数据作为任务输入。这使得Magnet能够容忍块 push 操作的部分完成。
  • 灵活的部署策略 - Magnet 通过在顶层构建的方式集成了 Spark 原生的 shuffle。这使得Magnet可以部署在具有相同位置的计算和存储节点的 on-prem 集群中与disaggrecated存储层的cloud-based的集群中。在前一种情况下,随着每次 Reduce Task 的大部分都合并在一个位置,Magnet利用这种本地性来调度 Reduce Task 并实现更好的 Reducer 数据本地性。在后一种情况下,代替数据本地性,Magnet可以选择较少负载的远程 shuffle 服务,从而更好的优化了负载均衡。
  • 缓解落后/数据倾斜 - Magnet可以处理落后和数据倾斜。由于Magnet可以容忍块 push 操作的部分完成,因此可以通过停止慢速 push 操作或跳过 push 大/倾斜的 block 块来缓解落后和数据倾斜。

3.2 Push-Merge Shuffle

为了提高磁盘 I/O 效率,我们需要增加每次 I/O 操作的数据量,或者切换到使用 SSD 或者 PMEM,这些存储介质优化了小的随机读。

PMEM,目前Intel中文官方命名为“英特尔傲腾持久内存”,简称为“持久内存”。英文官方名为 Intel Optane Persistent Memory。根据应用的特点,PMEM 比硬盘性能高,但是贵些;比内存价格便宜,但性能低些。

但是,正如第 2.2 节所指出的那样,由于“协调困境”的存在,应用程序的参数调整不能有效的增加 Shuffle 块大小,并且 SSD 或 PMEM 太昂贵,无法大规模的存储中间 Shuffle 数据。

下面 2 项研究提出了一个解决方案:合并属于同一个 Shuffle 分区的 Shuffle block 块,以便创建更大的数据块。

S. Rao, R. Ramakrishnan, A. Silberstein, M. Ovsiannikov, and D. Reeves. Sailfish: A framework for large scale data processing. Proceedings of the 3rd ACM Symposium on Cloud Computing, pages 1–14, 2012.

H. Zhang, B. Cho, E. Seyfe, A. Ching, and M. J.
Freedman. Riffle: optimized shuffle service for large-scale data analytics. Proceedings of the 13th EuroSys Conference, pages 1–15, 2018.

这种技术在 Shuffle 期间有效地提高了磁盘 I/O 效率。

Magnet采用 Push-Merge Shuffle 机制,也利用了 Shuffle 块合并技术。

相比于上面的研究来说,Magnet实现了更好的容错性,Reducer 数据本地性和资源利用效率。

关于这些比较的更多细节在第 6 节中给出。


3.2.1 为远程 push 准备 block 块

在设计 Magnet 的 block 块 push 操作时我们遵循的一个原则是尽可能减少 shuffle 服务的 CPU/内存 开销。

如前所述,Spark ESS 是集群中横跨所有 Spark 应用程序的共享服务。

对于 Spark on YARN 部署,分配给 Spark ESS 的资源通常比 Spark Executor 容器可用的资源少几个数量级。

在 shuffle 服务上产生大量的 CPU/内存 开销会影响其可扩展性。

在下面的研究中,Spark ESS 需要从内存中的本地 shuffle 文件中缓冲多个 MB 大小的数据 chunk 块,以便有效地合并 block 块。

H. Zhang, B. Cho, E. Seyfe, A. Ching, and M. J.
Freedman. Riffle: optimized shuffle service for large-scale data analytics. Proceedings of the 13th EuroSys Conference, pages 1–15, 2018.

在下面的研究中,Mapper 产生的记录不是在本地物化的,而是发送到远程 shuffle 服务中的每个 shuffle 分区的写前缓冲区。在物化到外部分布式文件系统之前,这些缓冲的记录可能会在 shuffle 服务上进行排序。

S. Rao, R. Ramakrishnan, A. Silberstein, M. Ovsiannikov, and D. Reeves. Sailfish: A framework for large scale data processing. Proceedings of the 3rd ACM Symposium on Cloud Computing, pages 1–14, 2012.

这两种方法都不适合确保 shuffle 服务中的低 CPU/内存 开销。

Magnet 中,我们保留当前的 ​​shuffle 物化行为,它以基于排序的方式物化 shuffle 数据。

一旦 map 任务生成了 shuffle 文件,它会将 shuffle 数据文件中的 block 块划分为 MB 大小的 chunk 块,每个 chunk 块都被 push 到远程 Magnet shuffle service进行合并。

使用 Magnet,Spark Driver 确定一个 Magnet shuffle service 的列表,每一个给定 shuffle 的 map 任务都会与之呼应。

利用这个信息,每个 map 任务都可以一致性的决定怎样从 shuffle block 块映射到 chunk 块,怎样从 chunk 块映射到 Magnet shuffle service

更多的细节在算法 1 中描述。


算法 1:将 block 块切分成 chunk 块

Mapper 端可用的常量
  • Shuffle 分区数:R
  • 最大 chunk 大小:L
  • Shuffle 文件内部 Shuffle 块偏移量数组:l1,l2,…,lR
  • Driver 选择的 Shuffle 服务:M1,M2,…,Mn
变量

添加 block 块的当前 chunk 块:C <- {}
当前 chunk 块尺寸:lc <- 0
当前 shuffle 服务的索引: k <- 1

输出

chunk 块和关联的 Magnet shuffle 服务

伪代码
for i = 1...R do
    if (i - 1) / (R / n) + 1 > k and k < n then
        output chunk and its shuffle service (C,Mk);
        C <- {block i};
        lc <- li;
        k++;
    else if lc + li > L then
        output chunk and its shuffle service (C,Mk);
        C <- {block i};
        lc <- li;
    else
        C = C ∪ {block i};
        lc = lc + li;

output chunk and its shuffle service (C,Mk);

这个算法确保了每个 chunk 块只能包含 shuffle 文件中连续的 block 块,直至一定大小的尺寸,并且来自同一个 shuffle 分区不同 mapper 的 block 块,会被 push 到相同的 Magnet shuffle service

为了减少相同 Shuffle 分区不同 Map 任务的 block 块会在同一个时间被 push 到同一个 Magnet shuffle service 的机会,每个 Map 任务都会以随机的顺序处理 chunk 块。

一旦 chunk 块被切分并且完成随机化,Map 任务将其移交给一个专用的线程池处理这些 chunk 块的传输并完成。

每个 chunk 块从磁盘加载到内存中,其中 chunk 块内的各个 block 块被推到关联的Magnet shuffle service

请注意,这种 chunk 块缓冲发生在 Spark Executor 内部而不是Magnet shuffle service

有关这一点的更多细节在第 4.1 节中给出。


3.2.2 合并 Magnet shuffle service的 block 块

Magnet shuffle service一侧,对于正在主动合并的每个 Shuffle 分区,Magnet shuffle service会生成合并的 Shuffle 文件,用来添加所有接收的相应 block 块。

它还为每个主动合并的 Shuffle 分区维护一些元数据。

元数据包括一个位图追踪所有 Shuffle 合并块的 Mapper ID,一个位置偏移量,表示最新成功添加 Shuffle 块后 Shuffle 合并文件中的偏移量,以及追踪当前正在添加的 Shuffle 块的 Mapper ID:CurrentMapId

这份元数据的唯一键由 application IDshuffle IDshuffle partition ID 混合组成,并且放到一个 ConcurrentHashMap 中。

见图 5 中的说明。

在这里插入图片描述

图 5:对于每一个 Shuffle 分区,Magnet shuffle service都会维护一份 shuffle 合并文件和相关的元数据。

Magnet shuffle service接收到 block 块时,在尝试添加到对应的 shuffle 合并文件之前,它首先要检索相应的 Shuffle 分区元数据。

元数据可以帮助Magnet shuffle service正确处理一些潜在的异常场景。

位图可帮助Magnet shuffle service识别任何潜在的重复块,因此没有多余的数据会被写入 Shuffle 合并文件中。

此外,即使 Map 任务已经随机排序了 chunk 块,Magnet shuffle service仍然可以从不同的 Map 任务中接收同一个 shuffle 分区的多个 block 块。

发生这种情况时,CurrentMapId 元数据可以保证,在下一次写入磁盘之前,将一个 block 块完整地添加到 Shuffle 合并文件中。

并且,在遇到足以损坏整个 shuffle 合并文件的故障之前,可以将 block 块部分地添加到 Shuffle 合并文件中。

当发生这种情况时,位置偏移量会有助于将 Shuffle 合并文件带回到健康状态

下一个 block 块会从位置偏移量处开始添加,这可以有效地覆盖损坏的部分。

如果损坏的 block 块是最后一个的话,block 合并操作结束之后将截断损坏的部分。

通过追踪这份元数据,Magnet shuffle service可以在 block 块合并操作期间适宜地去处理重复,冲撞和故障的情况。


3.3 提升 Shuffle 的可靠性

Magnet 尽一切努力,可以回退来取出原始未合并的 shuffle block 块。

因此,block 块 push/merge 操作期间的任何失败都是非致命的:

  • 如果在物化 shuffle 数据之前,Map 任务发生故障,此时尚未触发远程的 block 块 push 操作。Magnet会正常的重试 Map 任务。
  • 如果在重试过几次之后,shuffle 块还是不能被 push 到远程的Magnet shuffle serviceMagnet 会放弃 push 这个 block 块和相关的 chunk 块。没有成功 push 的这些 block 块会按照原来的方式来获取。
  • 如果Magnet shuffle service发现到 block 块合并操作期间因为任何的重复,碰撞或故障导致 block 块未被合并,则会替代的去获取原始未合并的 block 块。
  • 如果 Reduce 任务无法获取到合并的 Shuffle block 块,则会回过头来去获取已合并的 Shuffle 块后面未合并的 Shuffle 块列表,这样的话,shuffle 获取失败的情况就不会发生了。

正如 3.2.2 节中介绍的,Magnet shuffle service 中追踪的元数据信息大部分情况下都提高了 Shuffle 服务针对合并故障的容错性。

Spark Driver 中追踪的这些额外的元数据有助于提高 Reduce 任务的容错能力。

如上面的图 4 所示,在步骤 6 中,Spark Driver 在Magnet shuffle service中检索到 MergeStatus 的列表,同时 Driver 会通知这些服务停止给这些 Shuffle 合并 block 块。

此外,每个 Map 任务完成的时候,它还向 Spark Driver 报告 MapStatus(图 4 中的步骤 5)。

对于一个有着 M 个 Map 任务和 R 个 Reduce 任务的 Shuffle 来说,Spark Driver 会收集 M 个 MapStatus 和 R 个 MergeStatus

这些元数据会告诉 Spark Driver 每个未合并的 Shuffle block 块和已合并的 Shuffle 文件的位置和大小,还有哪些 block 块会合并到每一个 Shuffle 合并文件中。

因此,Spark Driver 可以完整的看到,怎样去获取每个 Reduce 任务已合并的Shuffle 文件和未合并的 Shuffle 块。

当 Reduce 任务没能获取到 Shuffle 合并 block 块时,元数据便会能够回过头来获取原始的未合并的 block 块。

Magnet 尽最大可能有效地维护了两份 Shuffle 数据的副本。

尽管这有助于改善 Shuffle 的可靠性,但是它还提高了存储需求和 shuffle 数据的写入操作。

在实践中,前者不是一个主要问题。

Shuffle 数据只会临时地存储在磁盘上。

一旦 Spark 应用程序完成,它所有的 Shuffle 数据也会被删除。

尽管在 LinkedIn 的集群中 Spark 应用程序每天要 Shuffle PB 级别的数据,但是高峰时期 Shuffle 数据的存储需求也只有几百 TB。

增加的 Shuffle 数据存储需求也只是集群中总容量非常小的一部分。

我们会在第 4.3 节中进一步地讨论了写入操作增长的影响。


3.4 灵活的部署策略

在第 3.2-3.3 节中,我们已经展示了 Magnet 是怎样基于 Spark 原生 Shuffle 来构建的。

和下面的 2 项研究不同,Magnet 允许 Spark 原生地去管理 Shuffle 的各个方面,包括存储 Shuffle 数据,提供容错能力,还有可以追踪 Shuffle 数据的位置元数据信息。

Cosco: An efficient facebook-scale shuffle service.
https://databricks.com/session/cosco-an-efficient-facebook-scale-shuffle-service (Retrieved 02/20/2020).

S. Rao, R. Ramakrishnan, A. Silberstein, M. Ovsiannikov, and D. Reeves. Sailfish: A framework for large scale data processing. Proceedings of the 3rd ACM Symposium on Cloud Computing, pages 1–14, 2012.

在这种情况下,Spark 不依赖于外部的系统进行 Shuffle。

这允许灵活地将Magnet部署在计算/存储同一节点的 on-prem 集群和具有disaggregated storage layercloud-based的集群。

对于计算和存储同一个节点的on prem数据中心,Shuffle Reduce 任务的数据本地性可以带来很多好处。

其中包括提高 I/O 效率,并且由于绕过网络传输减少了 Shuffle 获取失败的情况。

通过利用 Spark 的位置感知任务调度并且基于 Spark Executor 的位置信息选择 Magnet shuffle service来 push Shuffle block 块,实现 Shuffle 数据本地性似乎微不足道。

详情可见这篇文档——Spark data locality documentation.

然而,Spark 的动态资源分配使其复杂化。

详情可以这篇文档——Spark dynamic resource allocation documentation.

动态分配的功能使得 Spark 在一段时间内如果没有任务运行,则释放空闲的 Executor,并且如果任务再次待办,则可以稍后重新启动 Executor。

这使得 Spark 应用程序在多租户集群中资源更加富裕。

在 LinkedIn 的 Spark 部署中启用了这个功能。

同样的,其他大规模的 Spark 部署也推荐这一点。

Netflix: Integrating spark at petabyte scale.

Tuning apache spark for large-scale workloads.

通过 Spark 动态分配,当 Driver 在 Shuffle Map Stage 的开头选择Magnet shuffle service列表(图 4 中的步骤 1)时,由于 Executor 在前一个 Stage 的结尾会释放,活跃的 Spark Executor 的数量可能小于需求的数量。

如果我们选择基于 Spark Executor 位置信息的 Magnet shuffle service,我们最终可能比需求的 Shuffle 服务更少。

为了解决这个问题,我们选择在活跃 Spark Executor 之外位置的Magnet shuffle service,并通过基于所选Magnet shuffle service位置信息的动态分配机制来启动 Spark Executor。

这样的话,我们基于Magnet shuffle service的位置信息来启动 Spark Executor,而不会去基于 Spark Executor 的位置信息来选择 Magnet shuffle service

由于Magnet和 Spark 原生的 Shuffle 集成,因此可以进行这种优化。

对于cloud-based的集群部署,计算和存储节点通常是分开的。

在这样的部署中, Shuffle 中间数据可以通过快速网络连接在disaggregated storage中物化。

P. Stuedi, A. Trivedi, J. Pfefferle, R. Stoica, B. Metzler, N. Ioannou, and I. Koltsidas. Crail: A high-performance i/o architecture for distributed data processing. IEEE Data Eng. Bull., 40(1):38–49, 2017.

Taking advantage of a disaggregated storage and compute architecture.

Shuffle Reduce 任务的数据本地性在这种设置中不再重要。

然而,Magnet仍然适合这种cloud-based的部署。

Magnet shuffle service在计算节点上运行,在 disaggregated storage 节点上面存储合并的 shuffle 文件。

通过读取更大的数据 chunk 块而不是横跨网络的细碎的 shuffle block 块,Magnet有助于更好地利用可用网络带宽。

此外,Spark Executor 在选择Magnet shuffle service的时候可以选择优化更好的负载均衡而不是数据本地性。

Spark Driver 可以查询可用Magnet shuffle service的负载,以便选择负载低的。

在我们的Magnet实现中,我们允许通过灵活的政策来选择Magnet shuffle service的位置。

因此,我们可以选择根据集群的部署模式要么优化数据本地性要么优化负载均衡,或者两者都有也行。


3.5 处理落后和数据倾斜

任务落后和数据倾斜在实际的数据处理中是很常见的问题,并且它们减慢了作业执行。

为了成为我们环境中实用的解决方案,Magnet需要能够处理这些潜在的问题。


3.5.1 任务落后

当所有的 Map 任务在 Shuffle Map Stage 结尾完成的时候,Shuffle block 块推送操作可能还没有完全完成。

此时有一批 Map 任务刚刚开始推送 block 块,也可能有落后者做不到足够快地推送 block 块。

不同于 Reduce 任务中的落后者,我们在 Shuffle Map Stage 结尾经历的任何延迟都将直接影响作业的运行时间。

图 6 就展示了这点。

在这里插入图片描述

图 6:处理 push 操作的落后者

为了缓解这样的落后,Magnet允许 Spark Driver 设置期望等待 block 块推送/合并操作的时间上限

在等待结束的时候,Spark Driver 会向给定 Shuffle 的所有已选Magnet shuffle service发出通知以停止合并新的 Shuffle block 块。

然而,它确保Magnet可以提供 push/merge shuffle 的大部分益处,同时将落后者的负面影响限制在 Spark 应用程序的运行时间内。


3.5.2 数据倾斜

一个或多个 Shuffle 分区明显大于其他的时候数据倾斜就诞生了。

Magnet 中,如果我们试图 push/merge 这样的分区,我们可能最终会用几十 GB 甚至 几百 GB 的数据来合并分区,这是不可取的。

存在着现有的解决方案来处理 Spark 的数据倾斜,例如 AQE(Adaptive Query Execution)。

通过 Spark 的 AQE 机制,关于每个 Shuffle 分区大小的统计信息在运行时会被收集,用来检测数据倾斜。

如果这样的数据倾斜被检测到,则触发 Shuffle 的算子(例如 join 或 group by),可以将倾斜的分区分为多个桶,以便将倾斜分区的计算扩展到多个 Reduce 任务上。

Magnet与这种减轻数据倾斜的解决方案集成的很好。

当按照算法 1 将 Map 任务的 Shuffle block 块切分成 chunk 块时,Magnet可以通过不包括那些超过设定阈值的 shuffle block 块来修改算法。

这样的话,Magnet将合并所有正常的分区,但跳过那些超过尺寸阈值的倾斜分区。

通过 AQE 机制,非倾斜的分区仍然会被整体获取,这可以从Magnetpush-merge shuffle中受益。

对于倾斜的分区,由于Magnet不合并它们并且原始未合并的 block 块仍然可用,因此不会干扰 Spark AQE 减轻数据倾斜的解决方案。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值