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

131 阅读4分钟

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

Exactly Once 语义在 Flink 中的实现

数据流和动态表

数据流: 数据流(data stream)是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

动态表: 动态表 (Dynamic Table)是 Flink 的支持流数据的 Table API 和 SQL 的核心概念。与表示批处理数据的静态表不同,动态表是随时间变化的。可以像查询静态批处理表一样查询它们。查询动态表将生成一个 连续查询 。一个连续查询永远不会终止,结果会生成一个动态表。

下面是流和动态表之间的关系:

graph LR;
A(数据流)
B(动态表)
C(连续查询)
D(动态表)
E(新的数据流)
A --> B --> C --> D --> E
  1. 将流转换为动态表。
  2. 在动态表上计算一个连续查询,生成一个新的动态表。
  3. 生成的动态表被转换回流。

表 -> 流

若要使用关系查询就要讲流转化为表,转化为表的操作很简单,将 流的每一条记录都当作对表的INSERT操作

如下图:

流表转换

将每一个点击事件记录当作INSERT语句插入表中,而右侧的表因为不断的INSERT持续增长。

流 -> 表

动态表可以像普通数据库表一样通过 INSERT、UPDATE 和 DELETE 来不断修改。

在将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。Flink的 Table API 和 SQL 支持三种方式来编码一个动态表的变化:

  • Append-only 流: 只使用 INSERT 操作修改的动态表可以通过 输出插入的行 转换为流。

  • Retract 流: retract 流包含两种类型的 message: add messagesretract messages

    通过将INSERT 操作编码为 add message、将 DELETE 操作编码为 retract message、将 UPDATE 操作编码为更新(先前)行的 retract message 和更新(新)行的 add message,将动态表转换为 retract 流。

  • Upsert 流: upsert 流包含两种类型的 message: upsert messagesdelete messages

    转换为 upsert 流的动态表需要(可能是组合的)唯一键。通过将 INSERT 和 UPDATE 操作编码为 upsert message,将 DELETE 操作编码为 delete message ,将具有唯一键的动态表转换为流。消费流的算子需要知道唯一键的属性,以便正确地应用 message。与 retract 流的主要区别在于 UPDATE 操作是用单个 message 编码的,因此效率更高。


数据处理保证的语义

  • At-most-once: 出现故障什么也不做,不保证任何语义,处理延时低
  • At-least-once: 保证每条数据均至少被处理一次,数据可能被重复处理。
  • EXactly-once: 数据有且只被消费一次。

快照和恢复

可以使用下面算法制作一个简单的快照:

  1. 暂停处理输入的数据
  2. 等待处理完所有已经输入的数据
  3. 第2步完成后,所有有状态的算子将状态保存到远端
  4. 恢复对输入数据的处理

状态恢复的时间: 等待所有处理逻辑消费完成 source 保留状态及之前的数据

Chandy-Lamport分布式快照算法

  • JobManager 发送 CheckPoint Barrier 标识到每一个source开始制作快照
  • 每个source保存好自己的状态后,向所有连接的下游继续发送 CheckPoint Barrier 并告知 JobManager 自己的状态已经完成
  • 当所有算子都告诉 JobManager 状态制作完成后,CheckPoint就完成了

端到端的Exactly-once语义

CheckPoint虽然能保证每条数据都对各个有状态的算子更新一次,但sink输出算子 仍然可能下发重复的数据

两阶段提交协议(two phase commit protocol,2PC)可以保证数据的强一致性,简单来说2PC就是 把分布式事务的提交过程分成表决和执行两个阶段。

预提交阶段:

  • 协作者向所有参与者发送一个commit消息
  • 每个参与者收到消息后,执行事务,但并不是真正的提交
  • 事务成功则向协作者发送一个 vote yes ,失败则发送一个 vote no

提交阶段:

若协作者成功接受到所有参与者的 vote yes 的消息

  • 协作者向所有参与者发送一个 commit消息
  • 每个收到commit的参与者 释放执行事务所需要的资源 ,并结束这次事务的执行
  • 完成2后,参与者发送一个 ack消息 给协作者
  • 协作者收到所有参与者的 ack消息 后,标识该事务已完成

若协作者有收到 vote no消息

  • 协作者向所有参与者发送一个 rollback消息
  • 每个收到roolback的参与者回滚事务,并释放资源
  • 完成2后,参与者发送一个 ack消息 给协作者
  • 协作者收到所有参与者的 ack消息 后,标识该事务已完成回滚

参考自官方文档