【论文翻译阅读】Apache Flink: Stream and Batch Processing in a Single Engine Apache Flink

Abstract

Apache Flink是一个用于处理流数据和批量数据的开源系统。Flink建立在这样一种理念之上:许多类别的数据处理应用程序,包括实时分析、持续数据流、历史数据处理(批处理)和迭代算法(机器学习、图分析),都可以表达并作为管道化的容错数据流来执行。在本文中,我们介绍了Flink的架构,并展开讨论了如何将一组(看似多样的)用例统一到一个单一的执行模型下。

Introduction

数据流处理(例如,复杂事件处理系统所示例的)和静态(批量)数据处理(例如,由MPP数据库和Hadoop所示例的)传统上被认为是两种非常不同的应用类型。它们使用不同的编程模型和API进行编程,并由不同的系统执行(例如,专用的流处理系统如Apache Storm、IBM Infosphere Streams、Microsoft StreamInsight或Streambase,与关系数据库或执行引擎相对,包括Apache Spark和Apache Drill)。传统上,批量数据分析占据了用例、数据量和市场的绝大部分,而流数据分析主要服务于专门的应用程序。
然而,越来越明显的是,当今大量的大规模数据处理用例处理的数据实际上是随着时间的推移不断产生的。这些持续的数据流来自于例如网络日志、应用程序日志、传感器,或者作为数据库中应用程序状态变化的变动(事务日志记录)。而不是将这些流当作数据流来处理,当今的设置忽视了数据生产的连续性和及时性。相反,数据记录通常被(往往人为地)批量化成静态数据集(例如,每小时、每天或每月的数据块),然后以一种对时间不敏感的方式进行处理。数据收集工具、工作流管理器和调度器协调批量的创建和处理,实际上是一个连续的数据处理管道。像“lambda架构”这样的架构模式结合了批处理和流处理系统来实现多路径计算:一个用于及时近似结果的流处理快速路径,和一个用于后期精确结果的批处理离线路径。所有这些方法都遭受高延迟(由批量处理引起)、高复杂性(连接和协调多个系统,以及两次实现业务逻辑),以及任意的不准确性,因为时间维度并没有被应用代码明确处理。
Apache Flink遵循一个范例,将数据流处理作为实时分析、连续流和批处理的统一模型,无论是在编程模型还是在执行引擎中。结合持久的消息队列,它们允许几乎任意重放数据流(如Apache Kafka或Amazon Kinesis),流处理程序在处理最新事件的实时性、连续地定期聚合大量数据,或处理数千兆字节的历史数据时不做区分。相反,这些不同类型的计算只是在持久流的不同点开始它们的处理,并在计算过程中维护不同形式的状态。通过高度灵活的窗口机制,Flink程序可以在同一操作中计算早期和近似的结果,以及延迟和精确的结果,从而避免了结合不同系统来处理两种用例的需要。Flink支持不同的时间概念(事件时间、摄取时间、处理时间),以便为程序员在定义事件如何相关时提供高度灵活性。
与此同时,Flink承认现在和将来都需要专门的批量处理(处理静态数据集)。对静态数据的复杂查询仍然适合批处理抽象。此外,无论是对流式用例的旧实现,还是对尚未知道如何在流数据上有效执行这种处理的分析应用,都仍然需要批处理。批处理程序是流处理程序的特例,其中流是有限的,并且记录的顺序和时间并不重要(所有记录隐含地属于一个包罗万象的窗口)。然而,为了支持具有竞争力的易用性和性能的批处理用例,Flink具有专门用于处理静态数据集的API,使用专门的数据结构和算法处理像连接或分组这样的运算符的批处理版本,并使用专门的调度策略。结果是,Flink呈现为一个全功能和高效的批处理器,建立在流运行时之上,包括用于图分析和机器学习的库。Flink起源于Stratosphere项目,是Apache软件基金会的顶级项目,由一个庞大而活跃的社区(在本文写作之时,有超过180名开源贡献者)开发和支持,并在多家公司投入生产使用。
本文的贡献如下所示:

  • 我们为流和批处理数据处理的统一架构提出了案例,包括一些仅与静态数据集相关的特定优化
  • 我们展示了如何将流处理、批处理、迭代和交互式分析表示为容错的流数据流(在第三节中)
  • 我们讨论了如何在这些数据流之上构建一个全功能的流分析系统,具有灵活的窗口机制(在第四节中),以及一个全功能的批处理器(在4.1节中),通过展示如何将流处理、批处理、迭代和交互式分析表示为数据流。

System Architecture

在本节中,我们描述了Flink作为一个软件栈和一个分布式系统的架构。虽然Flink的API栈持续增长,但我们可以区分四个主要层面:部署、核心、API和库。
Flink的运行时和API。图1展示了Flink的软件栈。Flink的核心是分布式数据流引擎,它执行数据流程序。一个Flink运行时程序是由有状态运算符组成的DAG(有向无环图),这些运算符通过数据流相连接。Flink有两个核心API:DataSet API用于处理有限的数据集(通常被称为批处理),以及DataStream API用于处理潜在无限的数据流(通常被称为流处理)。Flink的核心运行时引擎可以被看作是一个流数据流引擎,且DataSet和DataStream API都能创建可由引擎执行的运行时程序。因此,它作为抽象有界(批处理)和无界(流处理)处理的共同基础。在核心API之上,Flink还捆绑了针对特定领域的库和API,这些库和API生成DataSet和DataStream API程序,目前包括FlinkML(用于机器学习)、Gelly(用于图处理)和Table(用于类似SQL的操作)。
image.png
如图2所示,一个Flink集群包含三种类型的进程:客户端、作业管理器(Job Manager)和至少一个任务管理器(Task Manager)。客户端将程序代码转换为数据流图,并将其提交给JobManager。数据流图的转换阶段还会检查运算符之间交换的数据的数据类型(模式),并创建序列化程序以及其他类型/模式特定的代码。DataSet程序另外还会经历一个基于代价的查询优化阶段,类似于关系型查询优化器执行的物理优化(更多细节请参见第4.1节)。
image.png
JobManager协调数据流的分布式执行。它跟踪每个运算符和数据流的状态和进度,安排新的运算符,并协调检查点和恢复。在高可用性设置中,JobManager在每个检查点将一组最小的元数据持久化到容错存储中,以便备用的JobManager可以重建检查点并从那里恢复数据流的执行。实际的数据处理发生在TaskManagers中。一个TaskManager执行一个或多个生成数据流的运算符,并向JobManager报告它们的状态。TaskManagers维护缓冲池以缓冲或实体化数据流,并维护网络连接以在运算符之间交换数据流。

介绍了 Flink 架构;从技术栈角度看,由 Stream/Batch API 撑起上层包括机器学习、TableSQL操作。同时说明了客户端、JobManager 与 TaskManager 是构成 Flink 最关键的三个进程模型

The Common Fabric: Streaming Dataflows

尽管用户可以使用多种API编写Flink程序,但所有Flink程序最终都会编译成一个通用表示:数据流图。数据流图由Flink的运行时引擎执行,这是批处理(DataSet)和流处理(DataStream)API下的共同底层层。

Dataflow Graphs

数据流图,如图3所示,是一个由以下部分组成的有向无环图(DAG):

  1. 有状态的运算符和
  2. 代表由运算符产生并可供运算符使用的数据的数据流。

由于数据流图是以数据并行的方式执行的,运算符被并行化为一个或多个称为子任务的并行实例,数据流被分割成一个或多个流分区(每个生成子任务一个分区)。这些有状态运算符(在特殊情况下可能是无状态的)实现了所有的处理逻辑(例如,过滤器、哈希连接和流窗口函数)。这些运算符中的许多是众所周知算法的教科书级实现。在第4节中,我们将提供窗口运算符实现的详细信息。数据流以各种模式在生产和消费运算符之间分配数据,例如点对点、广播、重新分区、扩散和合并。
image.png

Data Exchange through Intermediate Data Streams

Flink中的中间数据流是运算符之间数据交换的核心抽象。中间数据流代表由一个运算符产生的数据的逻辑句柄,可以被一个或多个运算符消费。中间流在逻辑上指向的数据可能会也可能不会在磁盘上实体化。数据流的具体行为是由Flink的上层参数化的(例如,DataSet API使用的程序优化器)。
管道化和阻塞数据交换。管道化中间流在并发运行的生产者和消费者之间交换数据,导致管道化执行。因此,管道化流通过中间缓冲池在消费者和生产者之间传递背压,以补偿短期吞吐量波动。Flink使用管道化流来实现连续的流处理程序,以及许多批处理数据流的部分,以避免在可能的情况下实体化数据。另一方面,阻塞流适用于有界数据流。阻塞流缓冲所有生产运算符的数据,然后再供消费,从而将生产和消费运算符分隔到不同的执行阶段。阻塞流自然需要更多的内存,经常溢写到次级存储,并且不传递背压。它们用于在需要时将连续的运算符彼此隔离,在计划中有可能导致分布式死锁的破坏管道运算符(如排序合并连接)的情况下使用。

管道化通常用于流式,延迟数据的产生以提高效率;另一方面对于有界数据量需要阻塞流来等待所有数据产生之后在供给消费。

平衡延迟和吞吐量。Flink的数据交换机制围绕缓冲区的交换实现。当生产侧的数据记录准备好时,它被序列化并分成一个或多个缓冲区(buffer)(一个缓冲区也可以容纳多个记录),可以转发给消费者。缓冲区要么在满时发送给消费者,要么在达到超时条件时发送。这使得Flink能够通过设置较高的缓冲区大小(例如,几千字节)来实现高吞吐量,同时通过设置较低的缓冲区超时(例如,几毫秒)来实现低延迟。图4显示了缓冲区超时对在30台机器(120核)上进行简单流式grep作业的记录传输吞吐量和延迟的影响。Flink可以实现20ms的99百分位延迟。相应的吞吐量是每秒150万事件。随着缓冲区超时的增加,我们看到延迟随着吞吐量的增加而增加,直到达到全吞吐量(即,缓冲区填充速度快于超时期满)。在缓冲区超时为50ms时,集群达到每秒超过8000万事件的吞吐量,99百分位延迟为50ms。

这些事件实际携带的数据量是多大?

image.png
控制事件。除了交换数据外,Flink中的流还通信不同类型的控制事件。这些是由运算符在数据流中注入的特殊事件,并且与流分区中的所有其他数据记录和事件一起按顺序交付。接收运算符通过在它们到达时执行某些动作来响应这些事件。Flink使用许多特殊类型的控制事件,包括:

  • 协调检查点(coordinator checkpoint)的检查点屏障(checkpoint barrier),通过将流划分为检查点前和检查点后(在第3.3节中讨论)
  • 水位线(watermarks),标志着流分区内事件时间的进展(在第4.1节中讨论),
  • 迭代屏障(iteration barrier),标志着流分区在循环数据流上的批量/过时同步并行迭代算法中达到了一个超级步骤的结束(在第5.3节中讨论)。

如上所述,控制事件假设流分区保留了记录的顺序。为此,Flink中消费单个流分区的一元运算符保证记录的FIFO顺序。然而,接收多个流分区的运算符按到达顺序合并流,以跟上流的速率并避免背压。因此,在经过任何形式的重新分区或广播之后,Flink中的流数据流不提供排序保证,处理乱序记录的责任留给了运算符实现。我们发现这种安排提供了最有效的设计,因为大多数运算符不需要确定性顺序(例如,哈希连接、映射),需要补偿乱序到达的运算符,如事件时间窗口,可以更有效地作为运算符逻辑的一部分来做到这一点。

控制流如果属于多个分区(keyBy)则不保序

Fault Tolerance

Flink提供了带有严格精确一次处理(exactly-once processing)一致性保证的可靠执行,并通过检查点(checkpointing)和部分重新执行来处理故障。系统为了有效提供这些保证,假设数据源是持久的且可重播的。例如文件和持久消息队列(如Apache Kafka)。在实践中,也可以通过在源运算符的状态中保持预写日志来结合非持久性源。
Apache Flink的检查点机制基于分布式一致性快照的概念,以实现精确一次处理保证。由于数据流的可能无限性质,恢复时的重新计算变得不切实际,因为长时间运行的作业可能需要重放数月的计算。为了限制恢复时间,Flink定期捕获运算符的状态快照,包括输入流的当前位置
核心挑战在于在不停止拓扑执行的情况下,取得所有并行运算符的一致性快照。本质上,所有运算符的快照应该指向计算中的相同逻辑时间。Flink使用的机制称为异步屏障快照(Asynchronous Barrier Snapshotting, ABS)。屏障是注入到输入流中的控制记录,对应于逻辑时间,并在逻辑上将流分为当前快照将包括的影响的部分和稍后将被快照的部分
运算符从上游接收屏障,并首先执行对齐阶段,确保已经接收到所有输入的屏障。然后,运算符将其状态(例如,滑动窗口的内容或自定义数据结构)写入到持久存储(例如,存储后端可以是外部系统,如HDFS)。一旦状态备份完毕,运算符将屏障向下游转发。最终,所有运算符将注册它们的状态快照,一个全局快照就完成了。例如,在图5中,我们展示了t2快照包含了所有运算符状态,这些状态是消费了t2屏障之前所有记录的结果。ABS与Chandy-Lamport算法用于异步分布式快照有相似之处。然而,由于Flink程序的DAG结构,ABS不需要检查点中的在途记录,而只依赖于对齐阶段将所有影响应用到运算符状态。这确保了需要写入可靠存储的数据保持在理论最小值(即,仅运算符的当前状态)。
image.png
从故障中恢复会将所有运算符状态还原到最后一次成功快照中的相应状态,并从最新的屏障开始重新启动输入流,该屏障已有快照。恢复时所需的最大重新计算量限制在两个连续屏障之间的输入记录量。此外,通过额外重放紧邻上游子任务缓冲的未处理记录,可以部分恢复失败的子任务。
ABS提供了几个好处:i) 它在从不暂停计算的情况下保证精确一次状态更新 ii) 它与其他形式的控制消息完全解耦(例如,触发窗口计算的事件,因此不限制窗口机制为检查点间隔的倍数)和 iii) 它与用于可靠存储的机制完全解耦,允许状态备份到文件系统、数据库等,这取决于使用Flink的更大环境。

Iterative Dataflows

增量处理和迭代对于图处理和机器学习等应用至关重要。数据并行处理平台对迭代的支持通常依赖于为每次迭代提交一个新任务,或者通过向运行中的DAG添加额外节点或反馈边来实现。在Flink中,迭代是作为迭代步骤实现的,这些特殊的运算符本身可以包含一个执行图(图6所示)。为了维持基于DAG的运行时和调度器,Flink允许迭代“头”和“尾”任务,它们通过反馈边隐式连接。这些任务的作用是建立指向迭代步骤的活动反馈通道,并为在此反馈通道内传输中的数据记录提供协调。协调对于实现任何类型的结构化并行迭代模型都是必需的,例如批量同步并行(BSP)模型,并使用控制事件来实现。我们将在第4.4节和第5.3节分别解释如何在DataStream和DataSet API中实现迭代。
image.png

Stream Analytics on Top of Dataflows

Flink 的 DataStream API 在 Flink 的运行时之上实现了一个完整的流分析框架,包括管理时间的机制,例如乱序事件处理、定义窗口以及维护和更新用户定义的状态。流API基于DataStream的概念,即一个给定类型的(可能是无界的)不可变元素集合。由于Flink的运行时已经支持流水线数据传输、连续的有状态运算符以及一致状态更新的容错机制,因此在其上叠加一个流处理器本质上归结为实现一个窗口系统和状态接口。如前所述,这些对于运行时是不可见的,它将窗口视为有状态运算符的一个实现。

The Notion of Time

Flink区分了两种时间概念:i) 事件时间(event-time),指的是事件发生的时间(例如,来自传感器的信号,如移动设备,所附加的时间戳),以及 ii) 处理时间(processing-time),即正在处理数据的机器的墙上时钟时间。
在分布式系统中,事件时间和处理时间之间存在任意的偏差。这种偏差可能意味着基于事件时间语义获得答案的任意延迟。为了避免任意延迟,这些系统定期插入称为低水位(low watermarks)的特殊事件,标记全局进度。例如,在时间进度的情况下,一个水位包含一个时间属性t,表明所有小于t的事件已经进入了一个操作符。水位帮助执行引擎以正确的事件顺序处理事件,并通过统一的进度度量来串行化操作,如基于窗口的计算。
水位在拓扑的源头产生,在那里我们可以确定未来元素固有的时间。水位从源头在数据流的其他操作符中传播。操作符决定它们如何响应水位。简单的操作,如map或filter只是转发它们收到的水位,而基于水位进行计算的更复杂的操作符(例如,事件时间窗口)首先计算由水位触发的结果,然后再转发它。如果一个操作有多个输入,系统只向操作符转发最小的传入水位,从而确保结果的正确性。
基于处理时间的Flink程序依赖于本地机器时钟,因此时间概念的可靠性较低,这可能导致在恢复时重放不一致。然而,它们的延迟较低。基于事件时间的程序提供了最可靠的语义,但可能由于事件时间和处理时间的延迟而表现出延迟。Flink还包括作为事件时间特殊情况的第三种时间概念,称为摄取时间(ingestion-time),即事件进入Flink的时间。它实现了比事件时间更低的处理延迟,并且与处理时间相比,结果更准确。

Stateful Stream Processing

虽然Flink的DataStream API中的大多数操作符看起来像是函数式的、无副作用的操作符,但它们提供了对高效有状态计算的支持。状态对许多应用程序而言至关重要,例如机器学习模型构建、图分析、用户会话处理和窗口聚合。根据用例的不同,存在着形形色色的状态类型。例如,状态可以是简单的计数器或求和,也可以是更复杂的东西,如分类树或机器学习应用中常用的大型稀疏矩阵。流窗口是有状态的操作符,将记录分配到作为操作符状态的一部分并在内存中不断更新的桶中。
在Flink中,状态是显式的,并通过以下方式在API中合并:i) 提供操作符接口或注释,以在操作符范围内静态注册显式局部变量和 ii) 提供操作符状态抽象,用于声明分区的键值状态及其关联操作。用户还可以使用系统提供的StateBackend抽象来配置状态的存储和检查点,从而允许在流应用程序中进行高度灵活的自定义状态管理。Flink的检查点机制(在第3.3节中讨论)保证任何注册的状态都是持久的,并具有精确一次更新语义。

Stream Windows

在Apache Flink中,对无界流的增量计算通常通过持续演变的逻辑视图进行评估,这种逻辑视图被称为窗口。Apache Flink在有状态操作符中整合了窗口功能,该操作符通过由三个核心功能组成的灵活声明进行配置:窗口分配器(assigner),以及可选的触发器(trigger)和驱逐器(evictor)。这三个功能可以从一系列常见的预定义实现中选择(例如,滑动时间窗口),或者可以由用户明确定义(即用户定义的函数)。
更具体地说,分配器负责将每条记录分配给逻辑窗口。例如,在事件时间窗口中,可以根据记录的时间戳做出这个决定。值得注意的是,在滑动窗口的情况下,一个元素可以属于多个逻辑窗口。可选的触发器定义了何时执行与窗口定义相关联的操作。最后,可选的驱逐器决定在每个窗口中保留哪些记录。Flink的窗口分配过程能够独特地覆盖所有已知的窗口类型,比如周期性的时间和计数窗口、标点、地标、会话和增量窗口。值得注意的是,Flink的窗口功能无缝地整合了乱序处理,类似于Google Cloud Dataflow,并且原则上包含了这些窗口模型。例如,以下是一个范围为6秒、每2秒滑动一次的窗口定义(分配器)。当水位标记通过窗口末端时计算窗口结果(触发器)。

1
2
3
stream
.window(SlidingTimeWindows.of(Time.of(6, SECONDS), Time.of(2, SECONDS)))
.trigger(EventTimeTrigger.create())

一个全局窗口创建一个单一的逻辑分组。以下示例定义了一个全局窗口(分配器),在每1000个事件时调用操作(触发器),同时保留最后100个元素(驱逐器)。

1
2
3
4
stream
.window(GlobalWindow.create())
.trigger(Count.of(1000))
.evict(Count.of(100))

请注意,如果上述流在窗口化之前按键进行分区,则上述窗口操作是本地的,因此不需要工作器之间的协调。这种机制可以用来实现各种窗口功能。

Flink通过窗口功能对无界流进行增量计算,通过窗口分配器、触发器和驱逐器这三种可配置的核心功能来实现。窗口分配器负责将数据记录分配到合适的逻辑窗口中,触发器定义了何时计算窗口内的数据,而驱逐器则确定窗口中应该保留哪些数据记录。Flink可以处理各种类型的窗口,并且能够无缝处理数据的乱序。用户可以自定义窗口行为,以满足不同场景的需求,同时Flink保证了分布式环境中不同工作器之间的操作可以是本地化的,降低了协调开销。这些窗口机制的灵活性和扩展性使 Flink 成为处理复杂窗口操作的强大平台。

Asynchronous Stream Iterations

流中的循环对于多种应用至关重要,例如增量构建和训练机器学习模型、强化学习和图近似。在大多数此类情况下,反馈循环不需要协调。异步迭代满足了流应用的通信需求,与基于有限数据的结构化迭代上的并行优化问题不同。如第3.4节和图6所展示的,Apache Flink的执行模型已经涵盖了异步迭代,在没有启用迭代控制机制的情况下。此外,为了符合容错保证,反馈流被视为隐式迭代头操作符的状态,并且是全局快照的一部分。DataStream API允许明确定义反馈流,并且可以轻松地包含对流上结构化循环的支持以及进度跟踪。
image.png

Batch Analytics on Top of Dataflows

有界数据集是无界数据流的一个特殊情况。因此,一个在窗口中插入其所有输入数据的流程序可以形成一个批处理程序,而批处理应该完全被Flink上文提到的特性所覆盖。然而,i) 语法(即批处理计算的API)可以简化(例如,不需要人为的全局窗口定义),ii) 处理有界数据集的程序可以进行额外优化,更高效的容错簿记以及分阶段调度。
Flink如下处理批处理计算:

  • 批处理计算由与流计算相同的运行时执行。运行时可执行文件可以使用阻塞数据流进行参数化,将大型计算分解为独立的阶段,这些阶段可以依次调度。
  • 当周期性快照的开销很高时,可以关闭它。相反,可以通过从最新的实体化中间流(可能是源)重新播放丢失的流分区来实现故障恢复。
  • 阻塞操作符(例如,排序)只是恰好在消耗完其所有输入之前阻塞的操作符实现。运行时并不知道操作符是否阻塞。这些操作符使用Flink提供的管理内存(在JVM堆上或堆外),如果输入超出内存限制,可以溢写到磁盘。
  • 专用的DataSet API为批处理计算提供了熟悉的抽象,即有界的容错DataSet数据结构和对DataSets的转换(例如,连接、聚合、迭代)。
  • 查询优化层将DataSet程序转换成有效的可执行文件。

下面我们将更详细地描述这些方面。

对批处理仍可以通过简化API、额外优化、更高效的容错记录和分阶段的调度进一步改善效率

Query Optimization

Flink的优化器采用了并行数据库系统中的技术,如计划等价性、成本模型和感兴趣属性(interesting-property)的传播。然而,构成Flink数据流程序的任意UDF(用户定义函数)重度依赖的DAG(有向无环图)不允许传统优化器直接使用数据库技术,因为操作符对优化器隐藏了它们的语义。出于同样的原因,基数和成本估算方法同样难以使用。Flink的运行时支持多种执行策略,包括重新分配和广播数据传输,以及基于排序的分组和基于排序和哈希的连接实现。Flink的优化器基于感兴趣属性传播的概念枚举不同的物理计划,并使用基于成本的方法在多个物理计划之间进行选择。成本包括网络和磁盘I/O以及CPU成本。为了克服存在UDF时的基数估计问题,Flink的优化器可以使用程序员提供的提示(hints)

Memory Management

基于数据库技术,Flink将数据序列化到内存段(memory segments)中,而不是在JVM堆中分配对象来表示缓冲中的数据记录。操作,如排序和连接,尽可能直接在二进制数据上进行,保持序列化和反序列化的开销最小,并在需要时将数据部分溢写到磁盘。为了处理任意对象,Flink使用类型推断和自定义序列化机制。通过保持数据处理在二进制表示并在堆外进行,Flink能够减少垃圾回收的开销,并使用高效利用缓存且鲁棒的算法,这些算法在内存压力下能够优雅地扩展。

Batch Iterations

过去,迭代图分析、并行梯度下降和优化技术已经在批量同步并行(Bulk Synchronous Parallel, BSP)和稳定同步并行(Stale Synchronous Parallel, SSP)模型之上实现。Flink的执行模型允许在其之上实现任何类型的结构化迭代逻辑,通过使用迭代控制事件来实现。例如,在BSP执行中,迭代控制事件标记了迭代计算中超步(supersteps)的开始和结束。最后,Flink引入了进一步的新颖优化技术,例如delta迭代的概念,它能够利用稀疏计算依赖性。Delta迭代已经被Flink的图处理API Gelly所利用。

当今,有许多针对分布式批处理和流式分析处理的引擎。我们将主要的系统归类如下。
批处理。Apache Hadoop是最受欢迎的开源系统之一,用于大规模数据分析,基于MapReduce范式。Dryad引入了嵌入式用户定义函数(UDFs)到基于DAG的数据流中,并由SCOPE丰富,后者提供了一种语言和基于它之上的SQL优化器。Apache Tez可以被视为Dryad提出的思想的开源实现。MPP数据库和最近的开源实现,如Apache Drill和Impala,将API限制在SQL变体上。与Flink类似,Apache Spark是一个基于DAG执行引擎的数据处理框架,提供了一个SQL优化器,执行基于驱动的迭代,并将无界计算视为微批处理。与此相反,Flink是唯一一个集成了以下功能的系统:i) 利用管道流执行批处理和流工作负载的分布式数据流运行时,ii) 通过轻量级检查点实现精确一次状态一致性,iii) 原生迭代处理,iv) 高级窗口语义,支持乱序处理。
流处理。之前有很多关于学术和商业流处理系统的研究,例如SEEP、Naiad、Microsoft StreamInsight和IBM Streams。这些系统中的许多都是基于数据库社区的研究。上述系统大多数要么是学术原型,要么是封闭源代码的商业产品,或者不能在商品服务器集群上横向扩展计算。更近期的数据流处理方法允许横向扩展和具有较弱状态一致性保证的组合数据流操作符(例如,在Apache Storm和Samza中至少一次处理)。值得注意的是,”乱序处理”(OOP)等概念得到了显著关注,并被MillWheel采用,后者是Google内部版本的后来提供的Apache Beam/Google Dataflow的商业执行器。Millwheel是精确一次低延迟流处理和OOP的概念验证,因此对Flink的发展产生了很大影响。据我们所知,Flink是唯一一个开源项目,它支持事件时间和乱序事件处理,提供具有精确一次保证的一致管理状态,并实现了高吞吐量和低延迟,同时服务于批处理和流处理。