Flink Checkpoint 机制详解 | 青训营笔记

681 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第6天。

前言

在介绍Checkpoint 机制之前,我们先来了解一下 Flink 中的 state(状态)。

state泛指:Flink中有状态函数和运算符在各个元素(element)/事件(event)的处理过程中存储的数据(注意:状态数据可以修改和查询,可以自己维护,根据自己的业务场景,保存历史数据或者中间结果到状态(state)中);

  • 使用状态计算的例子:

    • 当应用程序搜索某些事件模式时,状态将存储到目前为止遇到的事件序列。
    • 在每分钟/小时/天聚合事件时,状态保存待处理的聚合。
    • 当在数据点流上训练机器学习模型时,状态保持模型参数的当前版本。
    • 当需要管理历史数据时,状态允许有效访问过去发生的事件。

通过案例进一步理解

无状态计算指的是数据进入Flink后经过算子时只需要对当前数据进行处理就能得到想要的结果;有状态计算就是需要和历史的一些状态或进行相关操作,才能计算出正确的结果;

  • 无状态计算的例子:

    • 比如:我们只是进行一个字符串拼接,输入 a,输出 a_666,输入b,输出 b_666输出的结果跟之前的状态没关系,符合幂等性。
    • 幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用;
  • 有状态计算的例子:

    • 以wordcount中计算pv/uv为例:
    • 输出的结果跟之前的状态有关系,不符合幂等性,访问多次,pv会增加;

为什么需要state管理

流式作业的特点是7*24小时运行,数据不重复消费,不丢失,保证只计算一次,数据实时产出不延迟,但是当状态很大,内存容量限制,或者实例运行奔溃,或需要扩展并发度等情况下,如何保证状态正确的管理,在任务重新执行的时候能正确执行,状态管理就显得尤为重要。

为什么需要Checkpoint

Flink是有状态的流计算处理引擎,每个算子Operator可能都需要记录自己的运行数据,并在接收到新流入的元素后不断更新自己的状态数据。
当分布式系统引入状态计算后,为了保证计算结果的正确性(特别是对于流处理系统,不可能每次系统故障后都从头开始计算),就必然要求系统具有容错性。
对于Flink来说,流式计算对稳定性敏感,尽管我们在编写作业时一定会做好防御性编程,如各种判空、边界条件、安全的类型转换、格式判断、异常捕获等,但是墨菲定律说得好:

Anything that can go wrong will go wrong.

换言之,我们写再多的防御性代码,也无法覆盖所有非法数据的可能性,何况外部环境(网络、磁盘等)也会出现不可预知的波动,因此需要一个机制保证节点保存在本地的状态不丢失。流处理中Exactly-Once语义的实现也要求作业从失败恢复后的状态要和失败前的状态一致。

Flink 的机制

image.png

保证分布式环境下各节点状态的容错通常是通过定期对作业状态和数据流进行快照实现的,常见的检查点算法有比如 Sync-and-Stop(SNS) 算法、Chandy-Lamport(CL) 算法。

Flink的Checkpoint机制是基于Chandy-Lamport算法的思想改进而来,引入了Checkpoint Barrier的概念,可以在不停止整个流处理系统的前提下,让每个节点独立建立检查点保存自身快照,并最终达到整个作业全局快照的状态。有了全局快照,当我们遇到故障或者重启的时候就可以直接从快照中恢复,这就是Flink容错的核心。

Checkpoint执行流程

  • 每个需要checkpoint的应用在启动时,Flink的JobManager为其创建一个 Checkpoint CoordinatorCheckpoint Coordinator全权负责本应用的快照制作。

image.png

Checkpoint Coordinator周期性的向该流应用的所有source算子发送Barrier

  • Barrier是Flink分布式快照的核心概念之一,称之为屏障或者数据栅栏(可以理解为快照的分界线)。Barrier是一种特殊的内部消息,在进行Checkpoint的时候Flink会在数据流源头处周期性地注入Barrier,这些Barrier会作为数据流的一部分,一起流向下游节点并且不影响正常的数据流。Barrier的作用是将无界数据流从时间上切分成多个窗口,每个窗口对应一系列连续的快照中的一个,每个Barrier都带有一个快照ID,一个Barrier生成之后,在这之前的数据都进入此快照,在这之后的数据则进入下一个快照。

image.png

如上图,Barrier-n跟随着数据流一起流动,当算子从输入流接收到Barrier-n后,就会停止接收数据并对当前自身的状态做一次快照,快照完成后再将Barrier-n以广播的形式传给下游节点。一旦作业的Sink算子接收到Barrier-n后,会向JobMnager发送一个消息,确认Barrier-n对应的快照完成。当作业中的所有Sink算子都确认后,意味一次全局快照也就完成。

  • 当一个算子有多个上游节点时,会接收到多个Barrier,这时候需要进行Barrier Align对齐操作。

image.png 如上图,一个算子有两个输入流,当算子从一个上游数据流接收到一个Barrier-n后,它不会立即向下游广播,而是先暂停对该数据流的处理,将到达的数据先缓存在Input Buffer中(因为这些数据属于下一次快照而不是当前快照,缓存数据可以不阻塞该数据流),直到从另外一个数据流中接收到Barrier-n,才会进行快照处理并将Barrier-n向下游发送。从这个流程可以看出,如果开启Barrier对齐后,算子由于需要等待所有输入节点的Barrier到来出现暂停,对整体的性能也会有一定的影响。

综上,Flink Checkpoint机制的核心思想实质上是通过Barrier来标记触发快照的时间点和对应需要进行快照的数据集,将数据流处理和快照操作解耦开来,从而最大程度降低快照对系统性能的影响。

Flink的一致性和Checkpoint机制有紧密的关系:

  • 当不开启Checkpoint时,节点发生故障时可能会导致数据丢失,这就是At-Most-Once
  • 当开启Checkpoint但不进行Barrier对齐时,对于有多个输入流的节点如果发生故障,会导致有一部分数据可能会被处理多次,这就是At-Least-Once
  • 当开启Checkpoint并进行Barrier对齐时,可以保证每条数据在故障恢复时只会被重放一次,这就是Exactly-Once

总结

期待大佬们的指正!