这是我参与「第四届青训营 」笔记创作活动的第7天
1. 背景
- Checkpoint是周期性操作,把state数据持久化存储
- 对于source来说,如果程序出现问题,Flink需要重启,从最新的Checkpoint处恢复数据
- 我们希望输出端两次Checkpoint之间的数据能够回滚,只有回滚之后才能与Flink程序重启的source策略一致,因为重启后source是回滚到上一次的Checkpoint对应的偏移量
- 因此如果不能回滚,就导致了数据的重复计算
2. 总结
- 程序重启之后恢复到上一次成功的Checkpoint处,现在程序是在两次Checkpoint之间失败了,从上一次Checkpoint成功处,程序又处理了部分数据并且输出到外部系统了,因此如果只是简单的把程序恢复到上次成功的Checkpoint处,会导致程序重复计算部分数据
- Checkpoint能保证每条数据都对各个有状态的算子更新一次,sink输出算子仍然可能下发重复的数据
- 严格意义上的端到端的Exactly-Once语义需要特殊的sink算子实现
3. 两阶段提交协议
在多个节点参与执行分布式系统中,为了协调每个节点都能同时执行或者回滚某个事物性的操作,引入了一个中心节点来统一执行所有节点的执行逻辑,这个中心节点叫做协作者(coordinator),被中心节点调度的其他业务节点叫做参与者(participant)
3.1 预提交阶段
- 协作者(JobManager)向所有参与者(operator)发送一个pre-commit(预提交)消息
- 参与者收到消息后,执行事务,但是不真正提交
- 若事务执行成功,发送一个成功的消息(vote yes);若事务执行失败,发送一个失败的消息(vote no)
3.2 提交阶段
- 若协作者接收到所有参与者vote yes的消息
- 协作者向所有参与者发送一个commit(提交)消息
- 每个收到commit消息的参与者释放执行事务所需的资源,并结束这次事务的执行(步骤②)
- 完成步骤②后,参与者发送一个ack消息给协作者
- 协作者收到所有参与者发来的ack消息后,标识该事务执行完成
- 若协作者收到了参与者的vote no消息(或者发生等待超时)
- 协作者向所有参与者发送一个rollback消息
- 每个收到rollback消息的参与者回滚事务的执行操作,并释放事务所占资源(步骤②)
- 完成步骤②后,参与者发送一个ack消息给协作者
- 协作者收到所有参与者的ack消息后,标识该事务回滚完成
3.3 Flink 中的 2PC Sink
3.4 Flink两阶段提交总结
- ①事务开启:在sink task向下游写数据之前,均会开启一个事务,后续所有写数据的操作均在这个事务中执行,事务未提交前,事务写入的数据下游不可读
- ②预提交阶段:JobManager开始下发Checkpoint Barrier,当各个处理逻辑接收到Barrier后停止处理后续数据,对当前状态制作快照,此时,sink也不再当前事务下继续处理数据(处理后续数据需要新打开下一个事务)。状态制作成功则向JobManager发送成功的消息,失败则发送失败的消息
- ③提交阶段:若JobManager收到所有预提交成功的消息,则向所有处理逻辑(包括sink)发送可以提交此次事务的消息,sink接收到消息后,则完成此次事务的提交,此时下游可以读到这次事务写入的数据;若JobManager有收到预提交失败的消息,则通知所有处理逻辑回滚这次事务的操作,此时sink则丢弃这次事务提交的数据