一 检查点的实现算法

  • 一种简单的想法(同步的思想)
  • 暂停应用
  • 保存状态到检查点
  • 再重新恢复应用(Spark Streaming)
  • Flink 的改进实现(异步的思想)
  • 基于 Chandy-Lamport 算法的分布式快照算法
  • 将检查点的保存和数据处理分离开,不暂停整个应用

1 检查点分界线

检查点分界线又称Checkpoint Barrier或检查点屏障。

Flink 的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开。

分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中,具体见如下例子。

现在是一个有两个输入流的应用程序,用并行度为2的 Source算子来读取,按照奇偶性进行keyBy分流、累加、输出:

flink程序挂了检查点启动 flink检查点算法_算法

针对这样一条数据流,如何实现检查点的保存操作:

  • JobManager 会向 Source 任务针对每一个并行任务,都发送一条带有新检查点ID 消息的检查点分界线(三角形,当前检查点分界线id=2,此处的2与数据无关),通过这种方式让检查点分界线跟随着数据向下游流动,达到来启动检查点的目的。
    之前编写的,间隔固定时间分配一个检查点的程序,就是间隔固定时间向数据源里面注入一个检查点分界线。

flink程序挂了检查点启动 flink检查点算法_检查点_02

  • 当检查点分界线路过sorce算子时,也即当source算子接收到检查点分界线后,source算子会将它们刚消费完的偏移量(状态)快照下来,比如消费完3和4,会将它们保存到远程存储(HDFS)。
    保存完成之后,source会向 JobManager发送一条通知,告诉它,这两个souce任务检查点已经保存完成了。
    之后检查点分界线,会跟随数据向下游流动,数据源在keyBy时遵循复制,向下游广播的原则。
    在source算子做完检查点保存操作之后,才会将id=2的检查点分界线向下游广播。
    广播之后,分界线会跟随数据流来到下一个算子,这里就涉及到了检查点分界线对齐概念。

flink程序挂了检查点启动 flink检查点算法_算法_03

  • 分界线对齐:barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达,如sum even会接受到上游传递过来的两条分界线,且两条检查点的分界线都是2,那么
  • 对于 barrier 已经到达的分区,继续到达的数据会被缓存
  • 而 barrier 尚未到达的分区,数据会被正常处理

当sum even接收到上游所有并行任务传递过来的检查点id,才会进行检查点保存操作(保存8)。

当接收到一个分界线2和数据4,数据4不能和数据8进行累加,分界线2之后的所有数据都不能进行累加,都需要缓存到sum even算子中,而橘色的检查点2之前的数据,都需要和数据8进行累加,这就是上面两句话的意思,同时也是检查点分界线名称的由来,其将数据流分隔开了。

以上就是检查点分界线对齐的操作,只有当接收到上游发送过来的全部检查点分界线,才会保存自己的状态,并向JobManager发送一个保存成功的通知。

这也启发我们,流中的每一个算子都需要高效的执行,这样才不会阻碍分界线和数据向下游传播,否则某一算子内部可能缓存着大量的数据,对整个程序的性能造成极大的影响,且可能会造成内存崩溃。

flink程序挂了检查点启动 flink检查点算法_大数据_04

  • 当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中,然后将 barrier 继续向下游转发。
    检查点分界线向下游传播的过程,不影响前面的聚合操作和source的消费操作。
    当检查点分界线来到sink以后,将sink的状态也保存下来,sink的并行任务会向JobManager发送通知,当JobManager接收到了所有6个并行任务发送过来的通知,其就可以真正将HDFS中的检查点标记为已完成,删除之前的检查点,只保留最近的一份。
    只要有一个并行任务保存失败,那么整个检查点的保存就是失败的。

flink程序挂了检查点启动 flink检查点算法_flink_05

  • 向下游转发检查点 barrier 后,任务继续正常的数据处理

flink程序挂了检查点启动 flink检查点算法_大数据_06

  • Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕,当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了。

flink程序挂了检查点启动 flink检查点算法_flink_07

综上可以看到,检查点分界线相当于一个巡视员,路过哪一个算子就对哪一个算子做快照,然后将快照发回JobManager。

分流时,将检查点分界线向下游复制,广播;合流时要进行检查点分界线的对齐,必须接收到上游所有并行任务发过来的检查点分界线,它才可以进行快照操作。所有并行任务都完成快照之后,本次检查点(id=2)才真正的完成。

检查点分界线实质上也是逻辑时钟的思想,逻辑时钟向下游传递,传到哪个算子,就对哪个算子做快照,当所有的算子都看到检查点分界线以后,检查点的保存才完成。

2 保存点

Flink 还提供了可以自定义的镜像保存功能,就是保存点(savepoints)。

检查点是自动保存,任务宕机之后,自动重启。

原则上,创建保存点使用的算法与检查点完全相同,只不过需要手动保存手动恢复,因此保存点可以认为就是具有一些额外元数据的检查点。

Flink 不会自动创建保存点,因此用户(或者外部调度程序)必须明确地触发创建操作,savepoint 是手动执行的。

那么为什么还需要使用保存点呢?当用户执行完保存点之后,可以在保存点处重新运行一份新的Flink程序,并不影响原程序的运行,这样,就可以对这两个程序进行比较,主要用于程序的升级、AB测试(面向数据决策)。

保存点是一个强大的功能。除了故障恢复外,保存点可以用于:有计划的手动备份,更新应用程序,版本迁移,暂停和重启应用,等等。