Exactly-Once在Flink中的实现介绍| 青训营笔记

71 阅读6分钟

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

 

1. 如何在数据流上执行SQL语句并说明流式处理中状态的概念

2. 发生故障时,Flink自身如何从故障中恢复并保证对数据消费不丢不重

3. Flink本身的checkpoint机制如何和外部存储结合,实现端到端的不丢不重

 

数据流和动态表

数据流→动态表→连续查询→动态表→数据流

 

1. 数据流转化为动态表

下图为事件流转化为动态表的示例:

微信截图_20220728110729.png

Retract消息(回撤消息),先回撤记录再输入新信息,进行更新。(新的流)

2. 动态表经连续查询形成新的动态表

微信截图_20220728115615.png

连续查询的工作不会停止,查询结果也会不断更新,并产生新的动态表

 

3. retract消息

上图的结果表的changelog流包含insert和delete操作。具体如下图:

retract.png

其中的delete操作就被称为retract(回溯)消息。而这个changelog流就是retract流。

 

另:changel流指的是包含INSERT/UPDATE/DELETE等的数据流。

· Append-only 流(只有 INSERT 消息)

· Retract 流(同时包含 INSERT 消息和 DELETE 消息)

· Upsert 流(同时包含 UPSERT 消息和 DELETE 消息)

查询过程中的一个概念:状态,保证之前的数据在之前的基础(状态)上进行更新。

 

4.数据处理保证

At-most-once:出现超时和错误时不会重试(不纠错)。处理时延低。

At-least-once:出现超时和错误时进行重试(比如重新处理、再次产生结果)。可能由于反馈失效而导致多次输入。

Exactly-once:即使重试发送消息,消息也会保证最多一次地传递给最终consumer。该语义是最理想的,但也难以实现,因为它需要消息系统本身与生产和消费消息的应用程序进行协作。

 

 

 

Exactly-Once和checkpoint

快照:指的是某瞬间的状态记录。

一般最简单的快照制作和使用:暂停处理数据,但需要等待后续的算子处理已输入的数据,再把每个算子的状态保存到远端存储。完成后再恢复数据处理。

其中的缺点:1.在大数据场景下,暂停等待下游数据处理后再存储,代价是巨大的。

Chandy-Lamport算法

开始制作快照,checkpoint注入 checkpoint输入.png 每个source算子都接受到JM发送的Checkpoint Barrier,表示状态快照制作的开始。

 

checkpoint开始传递 checkpoint传递.png

Source保存自己状态(到远端)后,向所有连接的下游传递Checkpoint Brrier,并告知JM自己状态已经制作完成。

 

 

多个source的checkpoint传递 checkpoint传递和数据运行.png

Source不止一个,因此同时输入的barrier经过source处理后进行传递会产生“副本barrier”,需要将“副本barrier”和实际流程中的barrier进行对齐,即“barrier alignment”。

算子会等待上游的barrier到达后才开始制造快照(对齐)。

已经制作完成的上游算子会继续处理数据,不会像“简单快照制作”那样因等待下游而阻塞。

 

实现了快照制作和处理数据的解耦。

  checkpoint的对齐.png

 

checkpoint对齐.png  

返还checkpoint.png

当所有算子都告知JM状态制作(快照)完成后,checkpoint就结束了。

 

 

影响:

1.事实上实现了快照制作和处理数据的解耦。已经制作完成的上游算子会继续处理数据,不会像“简单快照制作”那样因等待下游而阻塞。

2.在快照制作和BarrierAlignment过程中会因对齐需要而暂停处理数据,依旧有一定延迟。

3.快照保存到远端也可能极为耗时。

 

 

 

端到端的Exactly-Once语义(与外部存储结合)

Checkpoint能保证每条数据都对个个有状态的算子更新一次,但sink输出算子仍然可能下发重复数据。(at least once)

因此需要特殊的sink算子实现端到端Exactly-once。

 

通过两阶段提交协议实现端到端Exactly-once。

 

两阶段提交协议,分为预提交阶段和提交阶段。在多个节点参与执行的分布式系统中,为了协调每个节点都能同时执行或回滚操作,引入了中心节点统一协调所有节点的执行逻辑。

此中心节点称为“协作者”,被中心点调度的其他节点称为“参与者”。

协作者和参与者.png

 

预提交阶段:

1. 协作者向所有参与者发送commit消息

2. 每个参与者收到消息后,执行事务,但不真正提交

3. 若事务执行完成,发送“vote yes”,执行失败,则发送”vote no”

 

 

提交阶段:

【若协作者成功接收到所有的vote yes】

1.协作者向所有参与者发送commit消息

2.每个收到commit的参与者释放执行事务所需的资源,并结束执行

3.参与者发送ack给协作者

4.协作者收到参与者的所有ack后,标识该事务执行完成。

 

【若协作者接收到有vote no的消息或超时】

1.协作者向所有参与者发送rollback消息

2.每个收到rollback的参与者回滚事务的执行操作,并释放执行事务所需的资源

3.参与者发送ack给协作者

4.协作者收到参与者的所有ack后,标识该事务完成回滚。

 

两阶段1.png  

两阶段2.png

两阶段3.png

事物开启:在sink task向下游写数据前,开启一个事物,后续所有写数据的操作均在这个事务中执行,事物未提交前,事物写入的数据下游不可读。

 

预提交阶段:JM下发checkpoint barrier,当处理逻辑接收到barrier后停止处理后续数据,对当前状态制作快照,此时sink也不再当前事物下继续处理数据。状态(快照)制作成功则向JM发送成功的消息,失败则发送失败的消息。(即做备份)

 

提交阶段:若JM收到所有预提交成功的消息,则向所有处理逻辑发送可以提交此次事物的信息,sink接收到此消息后完成此次事物的提交,此时下游可以读到这次事物写入的数据;若JM有收到预提交失败的信息,则通知所有处理逻辑回滚这次的实务操作,此时sink丢弃提交的数据。(备份之后,可以继续往下运行,交与kafka(commit))

 

 

优势

1.严格意义上的端到端的Exactly-Once语义:下游读到的数据是不丢不重的。

2.增强的去重能力:可以在更长的时间维度对数据进行去重。