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

66 阅读5分钟

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

还是和往常一样,总结一下今天的上课内容,前面的文章中有提到了,Flink一个优点就是实现了Exactly Once,每条数据可以仅处理一次,我们来看一下它是怎么实现的。

数据流和动态表

我们先来看一下流的处理流程:

image.png

下图是数据流(Stream)转换为动态表(Dynamic Table)的示例

image.png

我们将流数据中的关键字提取出来,并生成对应数据表,每新增一条数据,就往表中插入一条对应的数据,可以看出这是一个实时动态的表,表中的数据是随时间变化的,通常可以称这些动态表为changelog Stream(流)

有了动态表后,我们就可以使用SQL查询,得到我们想要的的结果。

image.png 查询的表是动态的,得到的结果自然也是动态表,根据查询语句的不同,会输出不同的changelog流,一种就如上图所示,统计用户(user)对应用的点击次数(clicks),同一个用户多次点击,如Mary点击了url为./home的页面,接着又点击了url为./prod?id=1的页面,需要保存之前的统计状态,再更新(UPDATE)先前输出的结果。

另一种查询方式:

image.png

第二种查询,统计用户在固定时间内的点击次数,将结果添加到表中,对表的操作只包含INSERT操作

  • Retract(回撤)消息的产生 image.png

在这个例子中,对数据的更新使用了Retract的操作,在Mary点击了第二次页面后,先发送Retract(回撤)消息,再发送更新后的结果,这样流的下游就可以知道我对这条数据进行了更新。

不同数据处理保证的语义

但无可避免的,查询运行过程中会出现故障,应对故障的有三种处理方式:

  1. At-most-once:出现故障的时候,什么也不做,用于对数据准确度不敏感的场景。
  2. At-least-once:每条数据均会被处理一次,但一条数据可能存在重复消费。
  3. Exactly-once:每条数据都会均且仅处理一次,故障的影响可以几乎不计。

Exactly Once

状态的快照与恢复

image.png

Flink在流数据处理过程中,使用状态的快照和恢复来应对故障的发生。

在上图当中,Source算子负责读取数据流,sum_even算子负责统计数据的偶数和,sum_odd算子负责数据的奇数和。

在保存快照时,Source会暂停读取工作,记录它现在读取到的数据,也可以是在流数据中的位点,将其发送到远端可靠存储Storage,其他的算子也一样记录他们对应的数据,同时我们要保证所有算子对这个数据的处理已经结束了才能保存快照,像"564"这样保存的快照就是不正确的,没有等待奇数算子sum_odd处理完他的流程。

上述的快照保存方式有一个痛点,那就是在保存快照时,所有的算子都需要暂停它们的工作,等待所有算子执行完毕才能恢复流数据的处理,为此就引入了Chandy-Lamport算法

Chandy-Lamport算法

使用两条流的Chandy-Lamport算法流程:

image.png

  • 使用Checkpoint进行快照制作

image.png

Flink引入了Checkpoint来协助快照的制作,对每一个Source发送Checkpoint Barrier标识符表示快照制作开始,Source保存完自己的状态后做二个工作,向JM告知自己的状态已经保存完毕、向下游发送同样的Checkpoint Barrier标识符,完成这些任务后,Source算子就可以继续处理数据,收到标识符的算子则开始跟Source算子相同的工作保存状态。

当所有的算子都向JM告知自己的状态制作完成后,Checkpoint结束,完成快照的保存。

image.png

这种做法,每个算子只需要在接收到Checkpoint Barrier标识符后开始快照制作,不需要等待其他算子完成他们的状态制作,但可能会增加保存快照的时间。

使用Checkpoint保证了每个算子更新一次,但在Sink输出还会存在重复数据的输出,要实现严格的Exactly Once语义,就需要特殊的Sink算子。

两阶段提交协议

image.png 引入Coordinator(协作者)协调和回滚业务,被协作者调度的节点称为Participant(参与者),协助者首先向所有参与者发送任务,如张三向李四转账,需要扣除张三账上的余额,李四账户则加上这一笔钱。执行这2个操作的Participant确认业务后,向协作者发送已经准备就绪或操作无法执行即“VOTE YES/NO”,协作者收到后,再向参与者下达完成操作(COMMIT)的指令,参与者完成操作后再向协作者发送Ack确认,收到所有的Ack确认后,就可以认为这个业务执行完毕了。

  • VOTE NO

有一个或参与者表示VOTE NO,协作者就需要重启整个业务,发送ROLLBACK指令,确保不出现一方扣费了,另一方余额没涨类似的情况。

至此,Flink就实现了精确端对端的Exactly Once语义。