Flink中的Exactly Once语义 | 青训营笔记

107 阅读3分钟

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

一、数据流和动态表

SQL与流处理

处理数据有界性:SQL处理的表有界,流处理中流

数据流与动态表动态转换

  • Stream -> Dynamic Table -> Continuous Query(连续查询) -> Dynamic Table -> Stream
  • 动态表:随时间变化,可以像查询静态批处理表查询

二、Exactly-Once 和 Checkpoint

一致性保证语义

  • At-most-once:每条数据消费至多一次,处理延迟低
  • At-least-once:每条数据消费至少一次,一条数据可能存在重复消费
  • Exactly-once:每条数据都被消费且仅被消费一次,仿佛故障从未发

Chandy-Lamport算法

分布式快照算法

checkpoint

Flink通过checkpoint机制实现故障前后的状态快照制作和恢复

三、端到端Exactly-Once实现

语义

d294ba4bece74460a07549aaebc84583_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.webp

  1. Checkpoint保证每条数据对各个有状态的算子更新一次,sink输出算子仍可能下发重复的数据
  2. Exactly-Once语义需要特殊的sink算子实现

两阶段提交协议(2PC)

分布式协议。在多个节点参与执行的分布式系统中,为协调每个节点都能同时执行或回滚某个事务性的操作,引入一个中心节点来统一处理所有节点的执行逻辑,中心节点叫协作者coordinator,被中心节点调度的其他业务节点叫参与者。 (一)预提交阶段

  1. 协作者向所有参与者发送一个commit消息
  2. 每个参与者收到消息后执行事务,但不真正提交
  3. 事务执行完成,发送一个成功的消息;执行失败发送失败的消息 (二)提交阶段
  4. 协作者向所有参与者发送一个commit消息
  5. 每个收到commit消息的参与者回滚事务的执行操作,并释放事务所占资源
  6. 完成2后,参与者发送一个ack消息给协作者
  7. 协作者收到所有参与者的ack消息后,标识事务成功并完成回滚

Flink 中的2PC Sink

12a2bcac6d6a49eda631fd0260d64000_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.webp

  1. sink task向下游写数据前开启一事务,后续所有写数据在事务中执行,事务未提交前写入的数据下游不可读
  2. JM开始下发checkpoint barrier,当参与者接收到barrier后停止处理后续数据,对当前状态制作快照,状态制作成功则向JM发送成功的消息
  3. JM收到所有预提交成功的消息,则向所有处理逻辑发送可以提交此事务的消息,sink接受到消息后,则完成提交,下游可以读到事务写入的数据;若JM收到预提交失败的消息,通知所有处理逻辑回滚操作。

小结

  • 支持两阶段提交协议的下游存储可以结合checkpoint机制实现严格意义上端到端的Exactly-Once

四、Flink案例讲解

账单计算服务:场景简介

所有账单消息往kafka下发,从kafka读取账单消息,进行处理后写入MySQL

执行方案

  1. 在上次记录的位点之后,从kafka中读取固定大小的数据
  2. 对该批数据去重和聚合
  3. 处理完成后写入MySQL中,若全部写入成功,记录当前读取到的消息的终点位置;若写入失败则不记录
  4. 跳回步骤重复执行

存在问题

  1. 非严格意义上的端到端exactly-once语义,若批数据处理完成后,在写入MySQL中发生异常则存在部分数据写入的情况,下次作业启动后这部分数据依然会重复写入
  2. 去重能力有限,只能在当前处理的一批数据内去重,批与批之间不可

flink解决方案优点

  1. 语义;下游读到的数据不丢不重
  2. 可在更长的时间维度对数据进行去重