
摘 要 **:**本文整理自阿里云基础平台开发冯明潇老师在11月15日 Apache Spark & Paimon Meetup,助力 Lakehouse 架构生产落地上的分享。
本文从 Celeborn 的核心设计和关键机制等方面详细介绍了 Celeborn Spark 集成最新进展,解决了现有 Shuffle 局限性,显著提升了大数据引擎的性能、稳定性和灵活性。
EMR Serverless Spark 交流钉钉群:58570004119
Apache Spark & Paimon 交流钉钉群:41594456
01
Celeborn 简介
Celeborn 的现状
目前,Celeborn 已成为最受欢迎的 Remote Shuffle Service。许多国内外大厂已经在使用 Celeborn,除了阿里云、小红书、B站、Shopee、蔚来等已经登记的用户,也有部分用户出于各种因素暂未正式登记。
Pinterest 在 2024 年 10 月的 Apache Software Foundation CoC 上分享了他们在云原生环境部署 Celeborn 的使用效果。在引入 Celeborn 后,实现了约十倍的资源节省。另一位国内用户也分享过他们使用 Celeborn 的收益情况。他们目前 RSS 使用1/6的集群磁盘资源,接入集群1/3+的 Shuffle 流量,作业迁移到 RSS Shuffle 后,作业执行性能明显优化,每天所有作业99分位数耗时提升30%以上;在持续有新作业上线的情况下,作业资源占用呈现下降的趋势。还实现了 Task 失败数大幅降低,核心宽表提速明显,提速约90分钟以上。
我们内部也进行了多项测试,结果显示 Celeborn 在特定场景下能够实现更优的性能表现。然而,由于内部测试可能涉及更复杂的策略和针对性更强的测试场景,因此我们选择不在此处展示这些结果。我们认为,外部用户的测试结果更能反映广大用户在实际生产环境中的使用效果。

Celeborn 的诞生
熟悉 Apache Spark 的用户,对下图应该不陌生。Shuffle 的引入主要是为了解决数据分布问题,特别是在处理宽依赖和数据 Join 时,这些操作会导致数据分布性发生修改,从而需要进行 Shuffle。如下右图展示了 Spark Sort Shuffle 过程。从图中可以看出,对于每一个 Map Task 而言,必须进行 Sort,Sort 后的 Shuffle 文件会被写入本地存储。由于采用了 Sort 存储,最终生成的 Shuffle 文件不仅有序,而且按照多个 Reduce 角度进行了聚合。这种方法也暴露出了一些明显的局限性:首先 Sort 过程一定会有写放大;其次,为了获取每一个小片,系统需要建立相当于 Map Task 乘以 Reduce Task 数量的网络连接数,导致随机 IO;此外,在 ESS 原生的 Shuffle 机制中,整个 Shuffle 数据是单副本的,这意味着如果 ESS 挂了或者节点故障,就会导致作业失败。综上所述,现有的 Shuffle 方案在灵活性、稳定性和可扩展性方面存在不足之处。
为了解决现有 Shuffle 的局限性,Celeborn 应运而生。Celeborn 被定义为一种大数据中间数据服务,旨在显著提升大数据引擎的性能、稳定性和灵活性。其最核心的特点之一是引擎无关,它可以支持 Spark、Flink、Tez、MR,以及更多 Native 引擎。Celeborn 项目于2021年正式开源,并在2022年10月成功进入 Apache 孵化器进行孵化。经过持续的发展和完善,Celeborn 最终于2024年4月23日晋升为 Apache 顶级项目。

02
Celeborn 的架构与演化
Celeborn 架构
Celeborn 架构概览如下:左侧展示的是 Celeborn 集群,右侧则是计算集群。Celeborn 集群的 Master 节点支持单副本和多副本配置。在多副本模式下,系统采用基于 Raft 协议的状态同步机制,以确保在任一 Master 节点发生故障时,整个集群仍能保持稳定运行。所有 Worker 节点通过定期发送心跳信息向 Master 报告其资源状态。
对于用户作业,无论是 Flink、Spark 还是 Hadoop 等框架,均会引入一个名为 LifecycleManager 的组件,该组件作为插件集成到各个引擎中,无需对原有代码进行修改即可直接使用。当作业发起 Shuffle 读写请求时,这些请求首先会被路由至 LifecycleManager。LifecycleManager 负责管理当前作业自身的 Shuffle 数据,并与 Celeborn 集群交互,执行包括资源申请与释放在内的各项操作。

Celeborn 核心设计
✨ Push Shuffle & Reduce/Map Partition
Celeborn 的核心设计可以归纳为两个关键特性:一是 Push Shuffle 机制,二是支持 Reduce/Map Partition 两种模式。
在下图左侧的示例中,展示了 Push Shuffle 机制的具体实现。从 Spark 任务的角度来看,多个 Map Task 会将其需要写入某个 Reduce 的数据提前在 Celeborn 上申请资源,并将这些数据直接推送到相应的 Partition 中。例如,M1 到 MN 的所有 Map Task 会将其需要写给 Reduce1 的数据全部推送到 P1 Partition 中。这种机制不仅解除了对本地磁盘的依赖,还优化了数据聚合过程,提升了整体性能。
右侧的图则详细说明了 Celeborn 支持的两种 Partition 模式。右上角的图展示了基于 Reduce 视角的 Partition 模式。在这种模式下,所有的 Map Task 会根据 Reduce Task 的需求来组织 Partition。可以看到,M1 和 MN 将数据推送到 P1,然后由 R1 进行处理。而下方的图则展示了基于 Map 视角的 Partition 模式。在这种模式下,Partition 的数据组织是基于 Map Task 的视角。例如,M1 Task 将其所有 Shuffle 数据推送到 P1 Partition 中。对于某个特定的 Reduce Task 来说,它可能需要读取一个或多个 Partition 的数据。目前,我们在 Flink 中主要使用 Reduce Partition 模式,而在今天的讨论中,由于重点是 Spark,因此对这一部分的内容不会过多展开。

Celeborn 关键机制
✨ 异步推送 和 Exactly Once
为了实现 Celeborn 的高性能,我们设计并实现了多种机制。首先,我们引入了异步推送机制。如下左图,对于 Celeborn Worker 而言,所有来自 Client 的 Shuffle 数据都会装到一个 Data Queue 中。随后,这些数据会被写入一个 Push Pool。在整个过程中,Push 操作不会等待上一次的数据写入完成,而是直接将数据积累在队列中,并通过异步处理的方式进行。
其次,我们还采用了异步 Flush 机制。当 Celeborn Worker 接收到 Shuffle 数据后,会先将其存储在内存中,然后通知 Shuffle Client 数据已处理完毕。如果启用了数据副本机制,一旦数据成功进入 Worker 1 和 Worker 2 的内存,系统便会告知客户端数据处理已完成,并可以开始推送下一个批次的数据。此外,由于异步 Flush 机制可能会出现 Map Task 执行完毕时,Shuffle 数据尚未完全写入磁盘的情况。因此,我们还引入了异步 Commit 机制。当多个 Map Task 完成时,它们会向 LifecycleManager 发送消息,告知所有 Map Task 已完成。LifecycleManager 随后会与 Worker 通信,确保所有 Partition 的 Shuffle 数据均已落盘以便后续读取。
最后,我们还实现了异步读取机制。在 Client 读取 Shuffle 数据时,Celeborn Client 会在内存中提前开好缓冲区,并维持固定数量。当缓冲区空闲时,就会从 Celeborn Worker 拉取新的 Shuffle 数据。这样便实现了异步读取,使得 Spark 任务在处理 Shuffle 数据的同时,Shuffle 数据的读取操作可以并行进行,从而提高整体性能。
为了确保数据的正确性,我们实现了精确的 Shuffle 数据一次性消费语义。这一机制的核心在于每个 Map Task 在写入数据时,会给每条记录添加一个数据头。数据头包含三个字段:Map ID、Attempt ID 和 Batch ID。当整个 Task 写入完成后,Celeborn LifecycleManager 会将正确的 Attempt ID 传递给下游。这样,下游就能获取到成功写入的数据,跳过失败或异常的数据,从而实现数据的精确一次性处理。

✨ 多层存储
为了进一步提升 Celeborn 的整体性能,我们实现了一种多层存储机制。对于 Celeborn 中的每一个重要特性,我们通常会编写一个 Celeborn Improvement Proposal (CIP)。CIP-3 和 CIP-8 详细描述了多层存储机制。
下图展示了 Worker 数据处理流程。左侧虚线框表示 Worker 的内存部分,中间代表本地磁盘,而右侧则表示云端存储。当 Shuffle 数据进入 Celeborn Worker 时,Celeborn 会根据当前节点的状态来决定数据的存储位置。如果内存资源充足,Shuffle 数据将首先被写入内存,并且后续的读取操作也将直接从内存中完成,从而最大化性能。若 Worker 的本地磁盘空间有限,Shuffle 数据可以被直接写入远端存储,以最大化 Celeborn 的可用性并降低节点成本。
此外,我们还支持自定义不同存储层级的优先级,还支持当一个存储层级满的时候将数据 Evict 到另一个存储层级,以优化整体性能。目前,我们在远程存储方面即将完成对 S3 的支持,而在内存支持方面,相关功能已经处于发布状态。

✨ Spark Skew Optimization
为了进一步提升性能,我们对 Spark AQE 也进行了优化。AQE 的功能大家应该已经非常熟悉,因此我将直接重点介绍我们的改进措施。
下图展示了 Celeborn 是如何支持 Spark AQE 的。当发生数据倾斜时,意味着某个 Partition 的数据量超出预期,Celeborn 通过以下方式应对:当一个 Partition 的数据量达到预设阈值或满足特定条件时,该 Partition 会分裂出另一个 Partition。分裂出来的 Partition 对于原 Partition 而言仍然被视为同一个逻辑单元。Partition 分裂后的信息会被传递给 Spark 端,而 AQE 框架则依据 Map Task 返回的 States 来计算出需要读取的数据范围,例如 Map Index ID 和 End Index ID。基于这些信息,Spark 会从 Shuffle 文件中读取所需数据。在 Celeborn 的实现中,所有 Shuffle 数据被聚合并推送到某个 Worker 上,导致在 Celeborn Worker 上的 Shuffle 数据对于 Map ID 来说处于无序状态。因此,当前的 Celeborn 实现需要执行一次排序操作,以 Map ID 为基准对文件进行排序。尽管 Celeborn 提供了一种几乎不消耗内存且性能优秀的排序算法,但这个过程依然引入了显著的开销。针对上述问题,Celeborn 会提供一个 Spark Patch。该补丁旨在修改 AQE 框架的实现逻辑。具体来说,当检测到数据倾斜时,Celeborn 将调整 Spark 任务的切分策略,改为以文件中的 Chunk 为边界来启动新的任务。如图所示,假设存在多个 Splits,每个 Split 可能包含若干个 Partitions,那么 Reduce Task 在读取数据时将直接按照 Partition Chunk 边界进行处理,从而避免了不必要的排序过程,极大地提升了整体性能。

Celeborn 稳定性建设
✨ Spark Stage Rerun
在运维 Spark 集群时,我们深知任务失败被紧急呼叫的困扰。为了解决这些问题,Celeborn 现已支持 Spark Stage 重算。具体来说,当 Celeborn 遇到节点丢失或其他异常情况时,Celeborn Client 将抛出一个 FetchFailureException。FetchFailureException 在 Spark 框架中会被识别,并触发对上游 Stage 的重算。这一机制确保了即使在业务高峰期或集群资源波动的情况下,虽然可能会触发任务的重试,但不会导致整个作业级别的失败。通过这种方式,显著提升了系统的稳定性。

✨ Quota 管理
对于大多数用户而言,维护一个或多个 Spark 集群时,为了确保资源的有效利用,通常也需要部署相应数量的 Celeborn 集群。如果选择仅维护单一的 Celeborn 集群,则需要服务于多个业务部门,就需要引入一种机制来控制每个不同的部门能够使用的资源配额。Celeborn 目前已经支持 Quota 管理,采用多租户模式。支持动态配置资源配额。允许管理员根据实际需求调整 Quota,而无需重启 Celeborn 集群,支持数据库和文件方式。值得注意的是,当遇到整个 Quota 不生效的情况,Celeborn 支持 Shuffle 过程 Fallback 到 Spark 原生 Shuffle 逻辑。这一特性最大化保障作业正常运行。

✨ 反压和流控
为了进一步提升系统的稳定性,Celeborn 实现了反压和流控机制。反压机制通过监控 Worker 节点的 Direct Memory 使用情况来实现。具体而言,当 Direct Memory 的使用量超过预设阈值时,Celeborn 会暂停来自客户端的数据传输,并触发更为激进的数据刷新策略,将数据从内存中刷写到其他存储,从而释放 Direct Memory 空间,防止 Worker 节点因内存不足而崩溃,进而提高系统的整体稳定性。
此外,Celeborn 还引入了一种拥塞控制机制,以应对某些任务或租户的 Spark 作业发送数据过快的情况,这些情况可能会对 Celeborn 集群造成负面影响,或者超出配置的配额上限。该拥塞控制机制借鉴了 TCP 的设计理念,引入了慢启动 Slow Start 机制。在检测到拥塞时,系统会显著增加 Chunk 的发送数量,以便快速达到网络带宽的上限。这一机制不仅有助于维持系统的稳定性,还能够最大化性能表现。

✨ 故障自愈和无感升级
Celeborn 集群现已支持故障自愈和无感升级功能。在故障自愈机制中,当某个 Worker 节点出现异常情况(如磁盘损坏、高负载或内存故障)时,系统能够自动检测到该节点的异常状态,并将其从可用资源列表中移除。一旦该 Worker 恢复正常,它将重新注册并加入集群,继续提供服务。
如下右图展示的是无感升级,无感升级功能通过 Celeborn 提供的 HTTP 接口实现对特定 Worker 节点的状态转换。例如,可以将某一个 Worker 转换到 Decommission 状态,在此状态下,该 Worker 将停止接收新的任务请求,但会继续处理已有的读写任务直至完成。随后,该 Worker 将进入空闲状态。这一过程使得在大规模 Celeborn 集群中,可以分批次地对 Worker 节点进行升级操作,而无需中断整个业务流程。因此,即使在执行升级操作期间,业务系统仍能保持正常运行,从而实现了无缝升级体验。

数据安全
当一个 Celeborn 集群要给很多用户使用时,一定会涉及到数据安全问题。为了确保一个用户的作业无法访问其他用户的作业数据,我们引入了 CIP-2 来实现这一目标。具体实现机制如下:当一个作业启动后,它会在 Celeborn 集群中注册一个 Token。这个 Token 包含了当前作业的详细信息,如用户信息和作业标识。一旦生成 Token,该 Token 会在 Celeborn 集群内部进行传播,并且在Spark作业端也会被传播。随后,所有与该作业相关的操作都会验证这个 Token 的有效性。通过这种方式,可以确保用户 A 的作业无法访问用户 B 的 Shuffle 数据,从而实现了细粒度的数据隔离。
此外,Celeborn 还支持传输层的安全加密(TLS),以应对一些特定的安全威胁场景。例如,如果 Celeborn 集群需要通过公网进行通信,或者面临黑客攻击等风险,TLS 可以确保即使数据包被截获,攻击者也无法解密并获取实际的数据内容。这种端到端的加密机制为数据传输提供了额外的安全保障。
综上所述,Celeborn 不仅能够有效地实现多用户环境下的数据隔离,还能确保数据在传输过程中的安全性,从而满足企业级应用对数据保护的严格要求。

Gluten 集成
Celeborn 与 Gluten 已经在对接集成,并且取得了不错的效果。众所周知,原生 Gluten 的速度已经很快了,在 HDD 场景下,Celeborn + Gluten 性能提升了 8%到12%,在 SSD 场景目前提升相对较小,大约在1%,后续会持续优化。如下图所示,描述了 Celeborn 在 Gluten 里面集成的两个 Stage。在 Gluten 的执行过程中,会产生 Columnar Batch 数据。这些数据经过分区后,会通过调用 Celeborn SDK 的方式,在 Spill 或最后一次 Flush 时直接写入 Celeborn 集群。当 Gluten 作业继续进行时,也会通过 Celeborn SDK 从 Celeborn 集群中读取 Shuffle 数据,将其重新加载到 Gluten 引擎中。这一集成方案现已集成到 Gluten 社区。

总结下 Celeborn 的特性。Celeborn 是一个引擎无关的大数据中间数据服务,其设计与实现不依赖于任何特引擎。作为实现存算分离的核心组件,Celeborn 为多种计算引擎提供了强大的支持,包括但不限于 Apache Spark、Flink、MapReduce 和 Tez,并且未来有望扩展至更多引擎。Celeborn 的架构具备高可用能力。基于现有客户反馈,Celeborn 在性能方面表现优异,能够满足大规模数据处理的需求。
此外,Celeborn 支持多层存储以及高度容错能力,能够有效应对各种异常状况,从而最大限度地保证数据处理流程的连续性和数据完整性。Celeborn 还支持灵活部署,支持虚拟机环境、支持与现有 Hadoop 或 YARN 集群部署在一起,以及 Kubernetes 环境下的容器化部署。
Celeborn 还提供了一整套全面的 CLI 接口,涵盖丰富的 HTTP Endpoint 和 Metrics 数据,便于第三方系统集成及运维管理。总之,Celeborn 以其卓越的设计理念、强大的功能特性以及广泛的兼容性,在大数据处理领域展现出巨大潜力。
03
未来规划
Celeborn 的未来规划主要分为四大方向,涵盖了资源调度优化、存储多样性支持、计算引擎兼容性增强以及功能提升等方面。
在资源调度方面,Celeborn 计划支持动态扩缩容能力。当前版本中,一旦集群创建完成,无法根据业务负载或集群压力实现扩缩容,必须依赖外部干预,如运维人员手动操作或其他第三方系统的介入。我们旨在开发一套内置于 Celeborn 的解决方案,以实现对 Worker 节点数量的管理。此外,我们将优化细粒度升级,目前 Celeborn 支持分批实现节点更新,未来我们计划给节点打上 Tag,从而实现在一个 Celeborn 集群中实现灰度测试或A/B测试。
此外,Celeborn 正致力于支持更多存储类型,包括但不限于阿里云对象存储 OSS。同时,我们拟引入基于优先级的 I/O 调度,确保高优先级作业能够获得充足资源并快速完成处理,而不会受到低优先级任务的影响或干扰,这对于运行大量 Spark 作业的企业级应用尤为重要。
在计算引擎对接方面,目前,我们计划实现一套 Native SDK 方案,并计划在未来版本中正式支持 Gluten。值得注意的是,现有对于 Gluten 的支持是通过调用 Celeborn Java SDK 间接实现的;未来会有专门的 C++ 版本的 SDK。除此之外,Celeborn 还兼容了 Blaze(快手基于 Rust 语言开发的计算引擎)、Fusion(EMR Serverless Spark 的 Native 计算引擎)以及 Flash(Flink 内置的高性能执行引擎)。
最后,功能提升中包括 Celeborn 即将引入的三项重要特性。首先,我们将推出 Celeborn Dashboard,这是一个基于 Web 的前端界面,旨在提供对 Celeborn 集群状态的全面监控。该 Dashboard 不仅能够展示单个或多个 Celeborn 集群的整体健康状况,还具备管理功能,使得用户能够更直观地进行故障排查与日常运维工作。其次,Celeborn 计划开源一款混乱测试框架,它能够在模拟节点故障、网络错误或数据丢包等异常情况,从而增强 Celeborn 对各种异常情况的处理能力,提升 Celeborn 鲁棒性。最后一项是 Spilt for MergedData。当前在 Celeborn 实现中,每一个 Map Task 完成前的一小批数据,会被包装成包含多个 Partition 数据 的 Merged Data 数据结构,该结构 会被推送到 Celeborn Worker,若此时 Worker 节点出现异常,则可能导致整个 Merged Data 重试,导致更多的 Partition 写数据操作需要重试进而引发级联效应,导致大量 Partition 同时进入重试状态。后续我们会针对这一点进行重点优化。
以上就是关于 Celeborn 未来发展方向的主要介绍,欢迎大家点击原文链接访问 Celeborn 官网获取更多信息。
