Flink到底能不能实现exactly-once语义?

导读:假设有这么一个场景 , Flink 在完成了一次 checkpoint 后 , 第二次 checkpoint 前(此时两个 checkpoint 中间的数据已经处理了一部分了)Job 出现异常挂掉了 。 当 Job 恢复时就会从上一次成功的 checkpoint 处恢复 。 那这个时候刚才被处理的数据又会被处理一次 , 那这就不能称为 exactly-once 语义了?这就是很多人存在疑惑的地方 , 下面我将从以下几个点展开讨论:

  • Exactly-once 语义理解
  • Flink 失败恢复依赖于检查点机制 + 可部分重放的数据源
  • 场景分析
  • 那Flink如何实现端到端的exactly-once
Exactly-once 语义理解exactly-once 语义指的是即使在出现故障的情况下 , Flink 流应用程序中的所有算子都保证事件只会被“精确一次”(恰好一次 , 不多不少)地处理 。
Flink到底能不能实现exactly-once语义?文章插图
Flink 失败恢复依赖于检查点机制 + 可部分重放的数据源可部分重放的数据源:这个很好理解 , 比如上游数据源是 kafka , 其支持从指定的 offest 处重新开始消费数据的 。
检查点机制:检查点是 Flink 中 Fault Tolerance 的核心机制 , 其会随着时间推移不断的做 Checkpointing , 不断的产生 snapshot(快照)存储到 Statebackend 中 , 快照里面记录了包括:
  • 当前检查点开始时数据源(例如Kafka)中消息的offset
  • 记录了所有有状态的operator当前的状态信息(例如sum中的数值)
【Flink到底能不能实现exactly-once语义?】当 Job 挂掉时就会从上一次成功的 checkpoint 处恢复 。
Flink 提供了 Exactly once 特性 , 其通过 barrier 对齐后 , 进行checkpointing , 生成snapshot并持久化实现 。 Checkpointing 是 Flink 中 Fault Tolerance 的核心机制 , 关于详细的 Checkpointing 工作方式 , barrier 及如何做到 exactly-once 等可以查看 Flink中的Fault Tolerance 容错机制。
场景分析
Flink到底能不能实现exactly-once语义?文章插图
Flink任务失败恢复的时候状态恢复的过程
为了方便分析问题我们对场景进行简化 , 假设 kafka 的 partition 只有一个 , 并且 Kafka Source 流过来的所有数据都是,key 都是同一个"hello" 。 Job 中间有一个 operator 处理算子对相同的 Key 数据的值进行 Sum 计算 。
Flink到底能不能实现exactly-once语义?文章插图
1、当Job 在第一次进行 checkpoint 的时候 , 快照里面的状态是:
  • <1,<0,10>> :表示 checkpoint 的 ID是1 , kafka 的 paritition 0 消费到的 offest 是10
  • operator算子的状态是
2、假设 checkpoint1 到 checkpoint2 中间有10条数据 。 Job 消费到 offest 为 12 的时候 Job 出现异常挂掉了 , 这时 operator 的状态是. 。
3、接下来任务会从上一次成功的 checkpoint1 开始恢复 , 也就是说 kafka 的 offest 还是从10 开始 , 算子的状态则是恢复到。
4、接着进行数据处理 , 这个时候尽管部分数据会被重复消费多一遍 , 但算子的状态中 SUM 的结果是正确的 。 这是因为算子的状态也恢复到之前的状态了 , 所以对于状态来说确实是实现了 exactly-once语义. 。
结论:Flink 是支持 exactly-once 语义的 , 但其针对的是 Flink 应用内部的数据流处理(也就是Flink的状态state来说的) , 换句话说 , 事件的处理可以发生多次 , 但是该处理的结果只在持久化后端状态存储中反映一次 , Flink自身是无法保证端到端的exactly-once语义的 。
Flink到底能不能实现exactly-once语义?文章插图