【译】深入 Apache Flink 可伸缩状态

2017年2月发布的 Apache Flink 1.2.0 引入了对可伸缩状态的支持。这篇文章将对flink有状态的流处理和可伸缩状态进行详细阐述。

有状态的流处理介绍

我们可以将流处理中的状态看做算子中的一块能记住之前输入数据信息的内存。可以被用来影响之后的输入。

相比之下,无状态的算子只考虑他们当前的输入,没有更多上下文和之前的信息。举例一个简单的例子来解释其中的不同:我们考虑一个数据源,发出的event的echema是 e = {event_id: int, event_value: int}。我们的目的是:对于每个event取出event_value并流出。我们可以简单用 source-map-sink 的 pipeline 实现,其中map函数从event的提取event_value并且把它发送到下游的sink来输出。这就是个无状态的流处理过程。

但是,如果我们要求改job使得只输出比之前的event_value大哥event_value呢?在这种情况下我们的map函数显然需要一些方法来机制之前event中的event_value,这就是一种有状态的流处理。

这个例子表明状态是在很多有趣的用法中是一个基本的有用的概念。

Apache Flink 中的状态

Apache Flink 只一个可以进行大规模有状态的流处理分布式系统。对于可伸缩性,Flink 作业在逻辑上分解为一个算子图,并且在物理上每个算子的执行被分解未多个并行的算子事例。在概念上,Flink 中的每个并行算子事例是一个独立的任务,可以被调度到各个集群中的各个机器。

为了实现高吞吐和低延迟必须最小化任务之间的网络通信。在 Flink 中,用于流处理的网络通信仅发生在作业的算子图中的逻辑边上,从而可以从上游到下游的算子中传输流数据。

但是,并行事例之间没有通信。为了避免这种网络通信数据局部性是 Flink 的一个关键原则。并且对如何进行状态的存储和访问产生很大影响。

通过这种设计任务的所有状态数据是本地的,并且状态访问不需要任务之间的网络通信。避免这种流量对于像Flink这种大规模并行分布式系统的可伸缩性至关重要。

对于 Flink 的有状态流处理我们区分了两种不同类型的状态:operator state 和 keyed state。operator state按算子(subtask)的每个并行事历进行范围划分,keyed state可以被看作是已经被划分或者分片的operator state。每个key只有一个状态分区。我们可以用 operator state 很容易地实现我们之前的例子。

重新调整有状态的流处理作业

在状态流处理中改变并行度(即改变算子执行工作的并行子任务的数量)是非常简单的。只需要重启无状态算子的事例就可以,断开或连接上下游算子,如 图1A 所示。