这是我参与「第四届青训营 」笔记创作活动的第3天
Exactly Once 语义在 Flink 中的实现
数据流和动态表
数据流: 数据流(data stream)是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
动态表: 动态表 (Dynamic Table)是 Flink 的支持流数据的 Table API 和 SQL 的核心概念。与表示批处理数据的静态表不同,动态表是随时间变化的。可以像查询静态批处理表一样查询它们。查询动态表将生成一个 连续查询 。一个连续查询永远不会终止,结果会生成一个动态表。
下面是流和动态表之间的关系:
graph LR;
A(数据流)
B(动态表)
C(连续查询)
D(动态表)
E(新的数据流)
A --> B --> C --> D --> E
- 将流转换为动态表。
- 在动态表上计算一个连续查询,生成一个新的动态表。
- 生成的动态表被转换回流。
表 -> 流
若要使用关系查询就要讲流转化为表,转化为表的操作很简单,将 流的每一条记录都当作对表的INSERT操作 。
如下图:
将每一个点击事件记录当作INSERT语句插入表中,而右侧的表因为不断的INSERT持续增长。
流 -> 表
动态表可以像普通数据库表一样通过 INSERT、UPDATE 和 DELETE 来不断修改。
在将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。Flink的 Table API 和 SQL 支持三种方式来编码一个动态表的变化:
-
Append-only 流: 只使用 INSERT 操作修改的动态表可以通过 输出插入的行 转换为流。
-
Retract 流: retract 流包含两种类型的 message: add messages 和 retract messages 。
通过将INSERT 操作编码为 add message、将 DELETE 操作编码为 retract message、将 UPDATE 操作编码为更新(先前)行的 retract message 和更新(新)行的 add message,将动态表转换为 retract 流。
-
Upsert 流: upsert 流包含两种类型的 message: upsert messages 和 delete messages 。
转换为 upsert 流的动态表需要(可能是组合的)唯一键。通过将 INSERT 和 UPDATE 操作编码为 upsert message,将 DELETE 操作编码为 delete message ,将具有唯一键的动态表转换为流。消费流的算子需要知道唯一键的属性,以便正确地应用 message。与 retract 流的主要区别在于 UPDATE 操作是用单个 message 编码的,因此效率更高。
数据处理保证的语义
- At-most-once: 出现故障什么也不做,不保证任何语义,处理延时低
- At-least-once: 保证每条数据均至少被处理一次,数据可能被重复处理。
- EXactly-once: 数据有且只被消费一次。
快照和恢复
可以使用下面算法制作一个简单的快照:
- 暂停处理输入的数据
- 等待处理完所有已经输入的数据
- 第2步完成后,所有有状态的算子将状态保存到远端
- 恢复对输入数据的处理
状态恢复的时间: 等待所有处理逻辑消费完成 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消息 后,标识该事务已完成回滚