摘要:本文整理自阿里云智能 Flink 团队的郭伟杰老师和哔哩哔哩的蒋晓峰老师在 Flink Forward Asia 2024 核心技术 (一) 专场中的分享,他们也分别是 Apache Flink 和 Apache Celeborn 的 PMC member。内容主要分为以下几个部分:
1、低延迟的 Pipelined Shuffle
2、高吞吐的 Blocking Shuffle
3、流批一体的 Hybrid Shuffle
4、未来规划
Shuffle 是分布式系统中数据流转的关键技术之一,对作业性能有着极为重要的影响,在计算引擎中扮演着重要角色。自 Flink 诞生以来,已有十年的发展历程。在 Shuffle 技术方面,Flink 也经历了多种 Shuffle 模式的演变,进行了多轮迭代和优化,实现了许多创新:从最初的 Pipelined Shuffle,到 Blocking Shuffle,再到创新性地提出 Hybrid Shuffle。本次分享主要探讨 Flink 社区在 Shuffle 方向遇到的问题、解决方法,以及对未来的思考和规划。
01. 低延迟的 Pipelined Shuffle
在此之前我们先看一下 Flink Shuffle 的总体架构。Flink 是一个典型的 Master-Worker 架构,其中有两个重要的进程:JobManager 和 TaskManager,通常简称为 JM 和 TM。在 JM 和 TM 进程中,会启动众多组件。图中不同颜色代表了组件所负责的不同功能:蓝色和黄色部分代表与 Shuffle 框架相关的组件,而绿色部分则是与 Shuffle 框架无关的其他组件。
我们先来看蓝色部分,称之为 Pluggable Shuffle Service,即可扩展的 Shuffle 服务,它是在 Flink 社区的 Flip31 中被提出的。考虑到用户对 Shuffle 服务的扩展需求,从 Shuffle 的一些核心组件中提取了重要的扩展点,主要包括 Shuffle Master 和 Shuffle Environment 这两个接口。抽象出这两个扩展点有什么用呢?举个例子,Flink 默认的 Shuffle 是基于 Netty 的,也就是基于 TCP/IP 协议栈的,如果可以摒弃掉 TCP/IP 协议栈,比如有些机器上有高性能的 RDMA 网卡硬件,我们可以通过 Pluggable Shuffle Service 自定义网络传输层,将其改造成有着更高吞吐的基于 RDMA 的 Shuffle。
接下来,我们详细看看这两个接口。ShuffleMaster 位于 JobManager 中,是一个中心化的控制组件,主要负责管理整个集群的 Shuffle 元数据信息。而每个 TM 中都会启动一个 ShuffleEnvironment,它是为 Task 的网络层读写服务的,会为 Task 创建用于写的 ResultPartition 和用于读的 InputGate。这里可能会有一些误区,社区经常看到有些开发者认为 Shuffle 服务与作业相关,实际上 Flink 中 Shuffle Service 是集群粒度的。如果在 Session 集群中在 Job 粒度配置了不同的 Shuffle Service,其实是不生效的,因为 Session 集群在被拉起时 Shuffle Service 的类型就已经确定了。
最后看黄色部分,这部分是框架提供的公共组件,其中有两个需要特别注意:BufferPool 和 PartitionManager。BufferPool 顾名思义,是负责管理 TaskManager 上 Buffer 资源的。Flink 上下游之间交换数据是以 Buffer 为载体的,每个 Task 在写数据前必须从 BufferPool 申请内存用于写,读数据时同样需要从 BufferPool 申请内存用于读。而PartitionManager 主要负责管理 TM 侧的本地 Shuffle 资源,也即与 ResultPartition 相关的资源。
在这样的架构下,当一个作业被提交后,它会在 JobManager 中创建一个 JobMaster 对象,并开始调度。同时会向 ShuffleMaster 注册自己的 ResultPartition,并请求一些关于上游的信息,主要包括上游的位置以及如何与上游建立连接。这些信息会随着任务的部署传递到 TaskManager。到达 TaskManager 后,Task 会通过 ShuffleEnvironment 创建出对应的 ResultPartition 和 InputGate 实例,并向 PartitionManager 注册。当 Task 开始运行后,它会向 BufferPool 申请用于读写的 Buffer,与上下游周而复始地进行数据交换。
与传统的批式 Shuffle 相比,Flink 能进行流计算的关键在于其拥有低延迟的 Shuffle 服务 — Pipelined Shuffle。它是 Flink 流作业默认的 Shuffle Service,也是 Flink 能在流计算领域取得成功的关键因素之一。
Flink 作为一个实时计算引擎,对数据处理的延迟非常敏感,为此做了很多努力:
- 增强调度约束:要求上下游任务同时运行。图中可以看到,当数据从 Map 任务发出到达下游的 Partition 时,下游的 Reduce 任务已经在运行了。而传统批式 Shuffle 引擎通常要求 Map 任务全部结束后,Reduce 任务才能开始。通过增强调度约束,增加了资源开销,但实现了更低的延迟。
- 基于内存的数据交换:避免了读写磁盘的开销。Pipeline Shuffle 的上下游之间仅通过内存和网络进行交互,没有批式 Shuffle 中巨大的磁盘 I/O 开销,这对于实时任务来说至关重要。
- 支持定时 Flush:在 Flink 中,数据通过 Buffer 传输。通常情况下,需要将一个 Buffer 写满后,下游才能读取。但在实时任务中,如果某些算子的处理逻辑很复杂,可能只产出了部分数据就开始了冗长的计算逻辑。这时可以启动一个定时的 Flush 线程,将未写满的 Buffer Flush 下去,使下游实时看到最新数据。
通过这三点优化,Flink 实现了非常低延迟的 Shuffle 服务。然而,在低延迟情况下,当下游处理达到瓶颈时,会产生新的问题:如何处理内存积压?
我们可以将 Pipeline Shuffle 的上下游看作是一个基于内存交换数据的生产者-消费者。若生产者生产速度过快,而消费者消费速度较慢,数据就会在内存中积压,若不加处理,可能导致 OOM(内存溢出)。
为处理这一问题,Flink 社区引入了基于 Credit 的流控和反压机制。它本质上是一种协调机制。该机制引入了两个核心概念: Backlog 和 Credit。Backlog 用来通知下游,上游还有多少数据未被处理;而 Credit 是下游对上游的一种承诺,告诉上游自己可以接收多少数据。
以上图为例,生产者的生产队列中有 5 个 Buffer,它会通知下游:“我有五个 Buffer 还未被消费,快来拉取。”理想情况下,下游就应该来拉取这 5 个 Buffer。然而,现实往往没有这么美好,由于一个 TaskManager 上的所有任务需要共享网络内存资源,下游任务可能只申请到了三个 Buffe,它只能向上游承诺:“我只能消费三个 Buffer 的数据。”于是,上游任务会将这三个 Buffer 发送下去,即图中的 1、2、3 号数据被发送。此时,对于上游任务来说,可用的 Credit 已经减少到零,上游任务就不能再发送数据了。数据在上游的生产队列中堆积,最终会产生反压。
随着下游算子的处理,可能隔一段时间已经可以申请到 Buffer 了。假设现在又申请了三个 Buffer,下游任务就会获得三个新的 Credit,上游就可以继续发送数据了。通过这种机制,Flink 巧妙地协调了上下游任务之间生产和消费的速率。这里描述的是只有一对相邻的生产者和消费者的简化模型。实际上,数据在整个拓扑结构中是持续流动的,反压逻辑可能会从 Sink 任务反向影响到数据源(Source),导致数据源切断与外部系统的数据读取。
02. 高吞吐的 Blocking Shuffle
接下来,我想和大家分享一个可能很少有人知道的事实:Flink 最初是作为批处理引擎而被设计的。随着创始人团队的不断思考,发现其架构对实时计算来说有很大的潜力,因此不断增强其实时计算能力,最终才成就了今天的 Apache Flink。但这里要强调的是,Flink 社区从未放弃对批处理的支持。实际上,在最近的几个版本中,Flink 在批处理方面做了大量工作,因此社区也鼓励大家多多尝试 Flink 的批处理能力。
在 Flink Batch 中,最初的 Shuffle 实现叫做 Bounded Blocking Shuffle。以一个 MapReduce 任务为例,假设有三个 Map 任务,每个 Map 任务有两个 SubPartition(这里的 SubPartition 是逻辑上的,实际上数据会被写入与之对应的文件中),每个 SubPartition 都会在物理磁盘上产生一个文件。
和 Pipelined Shuffle 不同的是:当上游任务未完成时,下游任务无法调度。只有当上游任务全部完成后,下游任务才会启动。这种架构虽然简单,但已经能保证很大的吞吐量。但是它现在已经逐渐被淘汰了,主要问题有以下三点:
- 稳定性:每个 SubPartition 都会产生一个文件,这会对文件系统造成巨大压力,甚至可能导致操作系统中 inode 耗尽。而批作业往往并发度都比较高,在高并发作业中会尤为放大整个问题的影响。
- 性能:大量小文件和随机 IO 会严重拖慢作业执行的性能,尤其是在 HDD 磁盘上尤为明显。
- 资源:内存与并发耦合。由于每条数据都需要写入磁盘,为了提高 Buffer 利用率减少落盘开销,至少要等到一个 Buffer 写满后才将其写入磁盘。但这引出了一个问题:Flink 无法预测下一条数据将会被写入哪个 SubPartition。为了避免资源死锁,必须为每个 SubPartition 至少准备一个 Buffer。因此,所需的最小 Buffer 的数量与 SubPartition 的数量(即并发数)正相关。这带来的问题是很难配置 Network Memory 的大小,因为在 Flink 中,网络内存必须在作业启动之前就指定。今天运行一个作业,可能一切正常,但如果明天增加了并发度,作业可能会因为网络内存空间不足而无法运行。
为了解决这些问题,Flink 社区在 1.13 版本中提出了 Sort-Merge Shuffle,其整体架构如下所示。
相较于 Bounded Blocking Shuffle,一个明显的不同之处在于每个 MapTask 的数据现在都只写入一个文件中。这个文件既包含了 SubPartition 1 的数据,也包含了 SubPartition 2 的数据。当然,它仍然是 Blocking 的,必须等到上游数据全部处理完毕,下游才能进行处理。
先来看一下它的架构:在每个 ResultPartition 里,都引入了一个叫做 SortBuffer 的数据结构,它是一个固定数量的 Buffer 集合。当 ResultPartition 向 SortBuffer 写数据时,不需要关心这些数据是属于哪个 SubPartition 的。如上图所示,数据可能是随机来自不同的 SubPartition,但都被追加到同一个 SortBuffer 中了。当 SortBuffer 写满,或者达到一些特定条件之后,会将这些 Buffer 进行按照 SubPartition 的顺序进行排序。排序完成后溢写到磁盘中的 ShuffleFile 中,这里是一个追加写。每个 SortBuffer 写入磁盘之后,会形成一个 Region,它只是一个逻辑上的概念,实际上在文件中不同轮次追加写入的数据是连续的。
这样一个改变是如何解决刚才提到的三个问题的呢?我们来逐一进行分析。首先,来看稳定性问题,之前提到稳定性问题主要是由于产生的文件数量过多。之前同一个 ResultPartition 里每一个 SubPartition 都会产生独立的文件,而现在每个 ResultPartition 只会产生一个文件,这极大地减少了文件的数量。其次,是资源问题,之前提到的资源问题主要是因为每个 SubPartition 都需要一个 Buffer,这就导致了它所需的内存数量实际上与并发数成正比。但在当前的方案中,我们只需要固定大小的 SortBuffer,并且它包含多少个 Buffer 是一个可配置的参数。Buffer 数对性能有影响,但与并发数无关。通过这种方式,解决了资源问题。
最后,来讨论最重要的性能问题。这里引入了一个名为 IO Scheduling 的机制。每个下游在拉取数据时,会在上游注册一个 Reader。多个 Reader 注册的先后时机是不同的,但是进行调度时,如果按照注册顺序去读取 Shuffle 文件,就会产生严重的随机读。为了解决这个问题,Flink 中引入了一个名为 IO Scheduler 的组件。它包含一个线程,不断对读取操作进行调度,并决定哪个 Reader 先读,哪个后读。具体的算法类似于操作系统中对磁盘进行调度的电梯算法,这里以每个 Reader 在 Shuffle 文件中要消费的下一条数据对应的文件中的 Offset 为比较依据,一个 Reader 需要消费的 Offset 越小,它的优先级就越高。如图所示,Reader1 要消费的数据是图中绿线所指的部分。而 Reader3 由于注册上来的比较晚,它要消费的是黄线所指部分。Reader2 消费的最快,它可能已经消费到了蓝色部分。
在这样的情况下,当 IO Scheduler 进行调度时,会优先让 Reader1 消费,其次是 Reader3,最后才是 Reader2。这种做法在很大程度上实现了顺序读取。但是,如果大家对 Spark Shuffle 比较了解的话,会知道 Spark 的处理方式与 Flink 不同。Spark 的 Sort-Based Shuffle 也是将一个 Partition 的数据最终写入一个文件。但它每次发生溢写时都会先产生并写入一个临时文件,之后再进行归并,最后合成一个文件。因此,Spark 产生的文件严格保证了全局有序,而 Flink 产生的文件仅在 Region 内有序,全局来看是无序的。Spark 这种方式数据的顺序性好,但是多个下游同时拉取数据时会破环顺序读,而且需要额外的归并开销。经过 Flink 社区的测试,基于 IO 调度的方式在许多工作负载上表现得更为出色。
我们习惯把每个 SubPartition 独立写文件的方式称为 Hash-Based Shuffle,把排序后统一写文件的方式称为 Sort-Based Shuffle。
Hash-Based Shuffle 主要有三个特点:
- 中间文件数量多:与上下游并发成正比,导致文件系统压力大。
- 随机读:由于随机读现象严重,IO 性能受到影响。
- 资源与并行度耦合:容易遇到网络内存不足的问题,易用性差。
Sort-Based Shuffle 解决了这些问题:
- 追加写一个文件:所有数据追加写入一个文件中,显著减少文件数量。
- 顺序读取:通过 IO 调度机制,保证了顺序读取,提升了性能。
- 资源与并行度解耦:一个 SortBuffer 只需要固定大小的网络内存,与并发无关,用户体感更好。
我们知道流批一体化的计算引擎是未来的发展方向。流批一体化不仅体现在 SQL 和 Data Stream API 中,也应该在 Shuffle 方向上进行创新。因此,社区提出了所谓的 Hybrid Shuffle。
03. 流批一体的 Hybrid Shuffle
在介绍 Hybrid Shuffle 之前我们先来看下 Flink Shuffle 也就是前面分享的两种 Shuffle 模式 Pipelined Shuffle 和 Blocking Shuffle 各自的优缺点,以及为什么要引入 Hybrid Shuffle。
首先来看 Pipelined Shuffle, Pipelined Shuffle 的优点在于高性能,主要体现在支持上下游 Task 是同时运行的,大幅缩短任务的运行时间。同时,其数据直接读写内存,可以在上下游之间直接传递,不需要落盘。但是 Pipelined Shuffle 在批场景下存在严重问题:它要求上下游必须全部拿到资源,如果同时存在多个任务,每个任务只能拿到一部分资源,很容易在 Session 集群中形成资源调度的死锁。我们来看左下角这张图,Map1,Map2 和 Reduce1,Reduce2同时运行,其中Map1,Map2和 Reduce1 都拿到了资源,但是 Reduce2 没有拿到资源,那么 Map1,Map 2和 Reduce1必须等到 Reduce2 有资源了才能运行,而我们想要的是 Map1,Map2 和 Reduce1 拿到资源以后就可以直接运行,不必等到 Reduce2 拿到资源后再运行。
反观 Blocking Shuffle ,它的优势主要有两方面,第一点是资源适应性好,理论上我们可以用一个 Slot 执行完所有任务。第二点是容错代价低,数据支持重新消费,从而不必全图重启。相比于Piplined Shuffle,Blocking Shuffle 性能相对差些,主要是因为所有数据必须全部落盘导致 IO 开销较大,并且由于批任务按 Stage 调度,必须等待每个Stage 的长尾作业执行完成。如右下角图所示,Map1目前没有资源,Map2运行完以后要等待Map1拿到资源然后运行完才能运行 Reduce1和Reduce2,而我们想要的是 Map2拿到资源运行完以后能够立刻执行 Reduce2。由此可见,不管是流式 Shuffle 还是批式 Shuffle 在特定的情况下都会出现资源碎片的现象,虽然持有资源却不能够调度任务并执行,从而会造成资源浪费。
大规模 Flink 作业的 Shuffle 数据会占据相当一部分磁盘存储空间且大小难以预估,在以 Kubernetes 为代表的云原生环境下问题更为突出,我们通常难以决定 K8S Pod 的磁盘空间大小:如果配置过小,则会遇到存储空间不足的问题;如果配置过大,由于资源多是以 Pod 为粒度进行隔离,又造成了存储资源的浪费。从上图左下方图例来看,Pod 磁盘空间太小的话,会有大 Shuffle 量的作业因为磁盘空间不够导致作业失败。反之 Pod 磁盘容量过大,对于大多数作业而言是浪费的,从而降低了集群的磁盘资源利用率。
Flink 社区在 1.16 版本引入了 Hybrid Shuffle,它是 Blocking Shuffle 和 Pipelined Shuffle 的结合,让 Flink 批处理具备了更强大的能力。Hybrid Shuffle 的核心思想是打破调度约束,根据可用资源的情况来决定是否需要调度下游任务,同时在条件允许时支持全内存不落盘的数据传输。Hybrid Shuffle 将 Pipelined Shuffle 跟 Blocking Shuffle 的特点结合在一起,在资源充足的情况下,上下游的所有任务可以同时运行,它的性能跟流式 Pipeline Shuffle 类似。在资源受限的条件下,Hybrid Shuffle 可以先让上游执行,将数据落到磁盘之后,下游再进行消费。它可以自适应地在内存和磁盘层之间进行切换,并不是静态的一次性切换。我们在数据消费的过程中,可以随时在内存写满的状态下,切换到磁盘模式。当内存中的数据被消费,留出更多的空间后,它又可以切换回内存进行消费。
我们来看 Hybrid Shuffle 架构,在 Hybrid Shuffle 中引入了自适应的分层存储架构。在这套架构中,我们将 Shuffle 上下游间的数据交换过程,抽象为上游将数据写入某种存储当中、下游再从该存储中读取数据的过程。在分层自适应存储架构中,包含一个写端 Client 和一个读端 Client,主要负责向不同的存储介质写数据和读数据。在中间的存储层,隐藏了内部实现细节,具有统一的抽象。写端按照优先级,进行存储层的数据写入。如果遇到空间不足等问题,该存储层会反馈当前无法接收数据,然后继续写下一个优先级的存储层。通过分层存储加动态自适应的方式,我们将多种存储层的介质,进行融合和互补,满足我们在不同情况下的需求。其中,最常用的几个层是内存,磁盘和远程存储。
相比于传统的批式 Shuffle, Hybrid Shuffle 主要具备以下特点:
- 打破调度约束:Hybrid Shuffle 打破了 Pipelined Shuffle 所有 Task 必须同时调度,Blocking Shuffle 必须分 Stage 调度的约束:在资源充足时,上下游 Task 可以同时运行;在资源不足时,上下游 Task 可以分批先后执行
- IO开销小:Hybrid Shuffle 打破了批作业所有数据必须全部落盘并从磁盘消费数据的约束,在上下游同时运行的情况下,它支持直接从内存消费数据,从而在提升作业性能的同时大幅减少磁盘 IO 带来的额外开销。
综合而言,Hybrid Shuffle 的核心优势包括:
- 动态决定存储介质:优先选择高性能的存储层,在满足一定条件时及时从低优先级的层切回高优先级的层,实现多层之间自适应切换。
- 灵活可扩展:提供统一的存储层抽象,支持插件化的存储层,用户可以灵活扩展到新的存储系统。
- 智能的 Task 调度:只要上游 Task 被调度,下游 Task 能够在任何时间进行调度,在资源充足时上下游同时运行,而在资源紧张时上下游串行执行。
大规模 Flink 批处理往往依赖存算分离的 Remote Shuffle Service,而 Apache Celeborn 正是一款致力于提供通用的统一中间数据处理解决方案,Apache Celeborn 源于阿里云自研的 EMR Remote Shuffle Service,旨在解决大数据引擎处理中间数据遇到的性能、稳定性及弹性问题,定位是大数据引擎统一中间数据服务。Celeborn 之前提供的 Flink Remote Shuffle Service 是 Blocking 式调度的,不能做到边读边写,并且 Shuffle 数据全部写远程,导致额外开销较大。打破这种调度约束正是 Hybrid Shuffle 最擅长的地方。因此,Flink 社区与 Celeborn 社区通力合作,通过将 Celeborn 作为一个特殊的层,打造了 Hybrid Shuffle 和 Apache Celeborn 的集成模式:动态调度,能够支持边读边写,基于自适应分层存储,多层之间灵活切换,同时又是面向云原生环境友好的,达成了 Flink 社区和 Celeborn 社区的双赢。
众所周知,Flink 批作业可以在 Task 失败后尽可能的不重启上游(只要 ResultPartition 产出的数据还未丢失),但无法应对 JobManager 挂掉的情况,一旦 JobManager 发生故障,所有生产和消费进度都将丢失,批作业不得不从头开始计算,代价及其高昂。目前 Flink 社区在 Flink 1.20 版本中通过 FLIP-383引入了一种新的批处理作业恢复机制,使 Flink 批作业能够在 JobManager 故障转移后尽可能多地恢复进度,避免重新运行已经完成的 Task,从而大幅降低 JobManager Failover 的代价。
Celeborn 社区目前也正在支持 JobManager Failover,核心流程主要是分为三个步骤:
- 批作业正常执行时:Flink HA 服务将 JM 的状态例如ExecutionGraph、OperatorCoordinator 等信息以 JobEvent 方式写入到 JobEventStore 比如 HDFS、ZK等以便在 JM Crash 后根据这些状态进行恢复,同时,Celeborn Client 也会通过 JobEvent 把关键信息比如 Shuffle ID 以及 Partition 信息写入到 JobEventStore 里。
- JM 发生异常导致退出时:Flink HA 服务负责重新拉起 JobManager。
- JM 重启状态恢复时:JM 会重新建立与 Shuffle Service 和 TM 的连接,然后JM会根据之前记录的状态以及 TM 上已经存在的分区尝试恢复作业进度。此时 Celeborn Client 也会从 JobEventStore 里恢复自身的状态,确保 Celeborn 上存储的 Partition 信息能被完全恢复。
04. 未来规划
关于 Flink 社区在 Shuffle 方向的未来展望,我们可以从三个主要方面进行探讨:
1. 生态系统的整合
这里主要指与 Apache Celeborn 社区的整合:
- Celeborn 支持 Flink 2.0: Flink 已经发布了 2.0 版本,而当前 Celeborn 社区只支持 Flink 1.20 版本,因此首要任务是全面适配 Flink 2.0 版本。
- 推动 Celeborn 客户端与 Flink 版本的解耦:目前,每个 Flink 版本都需要维护一个相应的 Celeborn 客户端版本,这给开发者和用户带来了很多困扰。我们的目标是改善客户端的兼容性,使每个客户端能够支持多个 Flink 版本,减少频繁升级的需求。
2. 性能、可观测性和易用性
Hybrid Shuffle 与 AQE 的结合:
- Flink 在 AQE(Adaptive Query Execution)上做了大量工作,显著提升了性能并解决了许多 Bug。然而,Hybrid Shuffle 模式与 AQE 之间存在内在冲突,因为 Hybrid Shuffle 需要上下游任务同时启动,而 AQE 需要中间存在阻塞点以便调整执行计划。
- 我们观察到某些算子(如聚合算子)在批模式下天然就需要数据全部处理完才会产生输出,因此可以以这类算子为分界线,将整个拓扑划分为多个子拓扑。在子拓扑内部采用 Hybrid Shuffle 调度模式,在子拓扑之间进行阻塞,等待统计信息产生后再继续下游,给 AQE 足够的优化空间。
引入更多的内存指标:
- Flink 目前缺乏网络层相关指标,用户难以判断当前内存是否足够以及是否需要增加内存。社区计划引入一系列新的内存相关指标,帮助用户更好地了解 Shuffle 服务对作业的影响,以及是否需要增加网络层内存以提升作业性能。
实现内存与拓扑的完全解耦:
- 理想情况下,作业拓扑应该和网络层所需的内存完全解耦。BufferPool 为作业动态分配内存,在内存紧张时分配较少的内存,尽管性能可能暂时下降,但作业可以正常运行,待内存充足时重新调配更多的内存以提升性能。
3. 长期发展路线图
Hybrid Shuffle 的推广:
- Hybrid Shuffle 在最近几个版本中进行了很多优化。未来几个版本后,Flink 社区会考虑将其设为批作业默认的 Shuffle 机制。
- 当前的 Hybrid Shuffle 还只支持批作业,未来我们希望它能同时支持流作业。其实它现在已经能够基于内存进行数据交换,只是缺少对流中的特殊事件如 Checkpoint 的处理。
更加智能的调度和执行模式:
- 用户无需感知 Shuffle 的实现细节。例如,Planner 向引擎提供上下游数据之间的依赖关系,避免死锁,而不需要告知引擎应使用哪种 Shuffle 形式,引擎将自动进行调度策略和执行模式的决策。
- 用户无需配置 Shuffle,系统将自动决定是使用内存还是磁盘或者其他存储介质,并进行相应的调度。
通过这些改进和优化,Flink 社区希望在 Shuffle 方向上进一步提升系统的性能、可观测性和易用性,最终为用户提供一个更加智能和高效的计算引擎。
更多内容
活动推荐
阿里云基于 Apache Flink 构建的企业级产品-实时计算 Flink 版现开启活动:
新用户复制点击下方链接或者扫描二维码即可0元免费试用 Flink + Paimon
实时计算 Flink 版(3000CU*小时,3 个月内)
了解活动详情:https://free.aliyun.com/?utm_content=g_1000395379&productCode=sc
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。