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

116 阅读4分钟

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

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

我们在之前学习了Flink引擎的基本架构,知道了在一般的大数据开发中会有流、批、OLAP等业务场景,也稍微了解了Flink是如何做到流批一体的,今天我们来学Exactly Once语义在Flink中的实现。

数据流和动态表

在我们的日常生活中,流式数据随处可见,它是一种源源不断的数据,而在一般的数据表中存储的数据肯定是有限的,这就会产生矛盾。这里我们先讲解一下SQL和流处理的区别:

  • SQL处理的表是有界的,执行查询可以访问完整的数据,批处理查询产生固定大小结果后终止。

  • 流是一个无限元组序列,执行查询无法访问所有的数据,查询不断更新结果,永不终止

所以,就需要来让流和表能互相转换来处理,动态表为此产生了

在流上定义表

首先我们来介绍一下动态表

与表示批处理数据的静态表不同,动态表是随时间变化的。可以像查询静态批处理表一样查询它们

之后在用户的每一次使用动态表就会变长,记录用户的操作行为,所以是动态的

连续查询

动态表的产生离不开连续查询

  • 连续查询从不中止
  • 查询结果会不断更新,产生一个新的动态表、

在任何时候,连续查询的2结果在语义上与以批处理模式在输入表快照上执行的相同查询的结果相同。

也可以查询产生仅追加数据的动态表

不同数据处理保证的语义

在数据查询中,有时不可避免地会出现错误,不同地处理错误的方式,就会产生不同的语义

image.png

同时也解释了标题中的Exactly Once语义是什么意思。

Exactly-once和checkpoint

当出现错误时系统就要重启,回到之前的状态,这时我们就需要一个状态作为快照,让系统的恢复有依据。这个时间点当然不能任意去一个时间点,而是需要等待所有处理逻辑消费完成source保留状态及之前的数据

快照制作的开始

每一个source算子都接收到JM发送的CheckpointBarrier表示状态快照制作的开始

image.png

Source算子的处理

各个source保存自己状态后,向所有连接的下游继续发送Checkpoint Barrier,同时告知JM自己状态已经制作完成

image.png

Barrier Alignment

算子会等待所有上游的barrier到达后才开始快照的制作; 已经制作完成的上有算子会继续处理数据,并不会被下游算子制作快照的过程阻塞

image.png

checkpoint的结束

所有算子都告知JM状态制作完成后,整个Checkpoint就结束了

image.png

端到端Exactly-once实现

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

  • 严格意义的端到端的Exactly-once语义需要特殊的sink算子实现

两阶段提交协议

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

预提交阶段
  • 协作者向所有参与者发送一个commit消息;
  • 每个参与的写作者收到消息后,执行事务,但是不真正提交;
  • 发送事务执行的状态信息
提交阶段

当写作和成功接收到所有的参与者 vote yes的消息:

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

总结

  • 数据流可以转换成动态表,动态表也能重新转换成数据流
  • 处理无限数据流的算子可以是有状态的
  • Flink通过Checkpoint机制实现故障前后的状态快照制作和恢复
  • 支持两阶段提交协议的下游存储可以结合Flink Checkpoint机制实现严格意义上端到端的EXactly-Once语义实现