Checkpoints

Checkpoint 使 Flink 的状态具有良好的容错性,通过 checkpoint 机制,Flink 可以对作业的状态和计算位置进行恢复。

Checkpoint 在默认的情况下仅用于恢复失败的作业,并不保留,当程序取消时 checkpoint 就会被删除。但是,也可以通过配置来保留 checkpoint,这些被保留的 checkpoint 在作业失败或取消时不会被清除;这样,就可以使用改改 checkpoint 来恢复失败的作业。

使用 ExternalizedCheckpointCleanup 配置项,可以定义当作业取消时,对作业 checkpoint 的操作:

  • ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION:当作业取消时,保留作业的 checkpoint。这种情况下,需要手动清除该作业保留的 checkpoint。
  • ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION:当作业取消时,删除作业的 checkpoint。仅当作业失败时,作业的 checkpoint 才会被保留。

示例:设置在作业取消时,保留作业的 checkpoint

CheckpointConfig config = env.getCheckpointConfig(); config.setExternalizedCheckpointCleanup(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);

checkpoint 目录结构

checkpoint 由元数据文件、数据文件组成。可通过配置文件中 state.checkpoints.dir 配置项来指定元数据文件和数据文件的存储路径,另外也可以在代码中针对单个作业特别指定该配置项。

当前的 checkpoint 目录结构(由 FLINK-8531 引入):

/user-defined-checkpoint-dir
    /{job-id}
        |
        + --shared/
        + --taskowned/
        + --chk-1/
        + --chk-2/
        + --chk-3/
        ...

其中:

  • shared 目录保存了可能被多个 checkpoint 引用的文件
  • taskowned 保存了不会被 JobManager 删除的文件
  • exclusive 保存那些仅被单个 checkpoint 引用的我呢间

需要注意的是,checkpoint 目录不是公共 API 的一部分,因此可能在未来的 release 中进行改变。

示例:全局配置 checkpoint 文件的存储路径

state.checkpoints.dir: hdfs:///checkpoints/

示例:配置单个作业的 state backend

env.setStateBackend(new RocksDBStateBackend("hdfs:///checkpoints-data/"));

从保留的 checkpoint 中恢复状态

作业可以从 checkpoint 的元数据文件恢复运行。如果元数据文件中信息不充分,那么 JobManager 就需要使用相关的数据文件来恢复作业。

$ bin/flink run -s :checkpointMetaDataPath [:runArgs]

Checkpoint 与背压

通常情况下,对齐 checkpoint 的时长主要受 checkpointing 过程中的同步和异步两个部分的影响。然而,当 Flink 作业正运行在严重的背压下时,Checkpoint 端到端延迟的主要影响因子将会使传递 Checkpoint Barrier 到所有的算子 / 子任务的时间。

当这种情况发生并成为一个问题时,有三种方法可以解决这个问题:

  • 通过优化 Flink、调整 Flink 参数、JMV 参数或扩容的方式消除背压源头
  • 减少 Flink 作业中缓冲在 in-flight 数据的数据量
  • 启用非对齐 checkpoint

这些选项并不是互斥的,可以组合在一起使用。

缓冲 Debloating

Flink 1.14 引入了一个新的工具,用于自动控制在 Flink 算子 / 子任务之间缓冲的 in-flight 数据的数据量。缓冲区 Debloating 机制可以通过将属性 taskmanager.network.memory.buffer-debloat.enabled 设置为 true 来启用。

此特性对对齐和非对齐 checkpoint 都生效,并且在这两种情况下都能缩短 checkpointing 的时间,不过 debloating 的效果对于对齐 checkpoint 最明显。当在非对齐 checkpoint 情况下使用缓冲区 debloating 时,额外的好处是 checkpoint 大小会更小,并且恢复时间更快。

非对齐 checkpoints

从 Flink 1.11 开始,checkpoint 可以是非对齐的。非对齐的 checkpoint 将 in-flight 数据作为 checkpoint state 的一部分,允许 checkpoint barrier 跨越这些缓冲区。因此,checkpoint 时长变得与当前吞吐量五官,因为 checkpoint barrier 实际上已经不再嵌入到数据流之中了。

当 checkpoint 因为背压导致周期非常长时,应该使用非对齐 checkpoint。这样 checkpointing 时间基本上就与端到端延迟无关了。需要注意的是,非对齐 checkpointing 会增加状态存储的 I/O,因此当状态存储的 I/O 是整个 checkpoint 过程中真正的瓶颈时,不应该使用非赌气 checkpoint。

示例:在 flink-conf.yml 配置文件中增加配置开启非对齐 checkpoints

execution.checkpointing.unaligned: true

示例:在作业中开启非对齐 checkpoints

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // 启用非对齐 Checkpoint env.getCheckpointConfig().enableUnalignedCheckpoints();

对齐 checkpoint 的超时

在启用非对齐 checkpoint 后,仍然可以指定对齐 checkpoint 的超时。在启动时,每个 checkpoint 仍然是对齐 checkpoint,但是当全局 checkpoint 支持时间超过 aligned-checkpoint-timeout 时,如果对齐 checkpoint 还没完成,那么 checkpoint 将会转换为非对齐 checkpoint。

示例:在 flink-conf.yml 配置文件中启用 checkpoint 超时

execution.checkpointing.aligned-checkpoint-timeout: 30 s

示例:在作业中开启 checkpoint 超时

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.getCheckpointConfig().setAlignedCheckpointTimeout(Duration.ofSeconds(30));

限制
并发 checkpoint

Flink 当前并不支持并发的非对齐 checkpoint。因为非对齐 checkpoing 具有更可预测和更短的 checkpointing 时长,所以所根本就不需要并发的 checkpoint。

此外,savepoint 也不能与非对齐 checkpoint 同时发生,因为它们将会花费较长的时间。

与 watermark 的相互影响

非对齐 checkpoint 在恢复的过程中改变了关于 watermark 的一个隐式保证。

目前,Flink 确保了 watermark 作为恢复的第一步,而不是将最近的 watermark 存放在 Operator 中,以方便扩缩容。

在非对齐 checkpoint 中,当恢复时,Flink 会在恢复 in-flight 数据后再生成 watermark。如果 pipeline 中使用了对每条记录都应用最新的 watermark 的算子,将会生成与使用对齐 checkpoint 不同的结果。对于这种情况,解决方法是将 Watermark 存放在 OperatorState 中。在这种情况下,Watermark 应该使用单键 group 存放在 UnionState 以方便扩缩容。

单条记录运行时间很长的情况

尽管非对齐的 checkpoint barrier 可以越过队列中的所有其他记录。但如果当前记录需要大量时间进行处理,则对该 checkpoint 的处理仍可能延迟。

可能出现这种情况的场景:

  • 同时出发计时器,例如在窗口操作中
  • 等待多个网络缓冲区可用而被阻塞时

因此 Flink 无法中断单个记录的处理过程,所以非对齐的 checkpoint 必须等待当前处理中的记录被完全处理完成。这可能导致如下两种情况:

  • 单个记录的数量过大,超出单个网络缓冲区能序列化的上限
  • 在一个 floatMap 操作中,一条输入记录产生了过多的输出记录。

此时,背压会导致阻塞非对齐 checkpoint 直到处理单个输入记录所需的所有网络缓冲区可用。

这种情况也可能会发生在任何处理单条记录需要一定时间的情况下。因此,检查点的事件可能会比预期事件更长,或可能会有所不同。

部分未使用 checkpoint 的数据连接模式

有一部分包含属性的的连接无法与 Channel 中的数据一样保存在 Checkpoint 中。为了保留这些特性并且确保没有状态冲突或非预期的行为,非对齐 Checkpoint 对于这些类型的连接是禁用的。所有其他的交换仍然执行非对齐 Checkpoint。

  • 点对点连接: 我们目前没有任何对于点对点连接中有关数据有序性的强保证。然而,由于数据已经被前置的 Source 或者 KeyBy 等方式隐式地分组,一些用户会依靠这种特性在提供的有序性保证的同时将计算敏感型任务划分为更小的块。只要并行度不变,非对齐 Checkpoint 会保留这些特性,但如果发生扩缩容,则这些特性将会被改变。

示例:

flink读取Checkpoint启动_背压

在上图中的任务中,如果想将以上任务的并行度从 2 扩容到 3,那么需要根据 KeyGroup 将 KeyBy 的 channel 中的数据突然地划分为 3 个 channel 中去,这是没有问题的。但是,对于 forwrad 的 channel,因为根本没有使用 KeyContext,所以也没有任何记录被分配了哪些 KeyGroup,显然也无法计算它,因为无法保证 Key 仍然存在。

  • 广播 Connections:广播分区通常用于实现广播状态,它应该跨所有 Operator 都相同。但对于非对齐 checkpoint 来说,无法保证所有 Channel 中的记录都以相同的速率被消费。这可能导致某些 Task 已经应用了与特定广播事件对应的状态变更,而其他任务则没有。对于这种情况,Flink 将通过仅 checkpoint 有状态算子的 subtask 0 中状态的单份副本;在恢复时,将该份副本发往所有的 operator。

示例:因记录消费速率不一致,导致广播状态不一致的示例图

flink读取Checkpoint启动_非对齐checkpoint_02