flink 中的一个大的特性就是exactly-once的特性,我们在一般的流处理程序中,会有三种处理语义
- at most once : 至多一次,表示一条消息不管后续处理成功与否只会被消费处理一次,那么就存在数据丢失可能
- exactly once : 精确一次,表示一条消息从其消费到后续的处理成功,只会发生一次
- at least once :至少一次,表示一条消息从消费到后续的处理成功,可能会发生多次
我们在程序处理中,通常要求程序满足exactly-once,就是确保数据的准确性,不丢失,不重复,但是实现这样的功能是比较复杂的,在flink中,是如何提供精确一次的特性呢?
我觉得应该有两方面的因素,一个是flink的checkpoint特性,另外一个是两阶段提交,这两方面的组合,让flink有了精确一次的能力。
flink的checkpoint机制是把程序处理的中间状态保存下来,当程序失败可以从最新的checkpoint中恢复,通过checkpoint的机制,flink可以实现精确一次和至少一次的语义。
barrier对齐发生在一个处理节点需要接收上游不同处理节点的数据,由于不同的上游节点数据处理速度不一致,那么就会导致下游节点接收到 barrier的时间点也会不一致,这时候就需要使用barrier对齐机制:在同一checkpoint中,先到达的barrier是否需要等待其他处理节点barrier达到后在发送后续数据,barrier将数据流分为前后两个checkpoint(chk n,chk n+1)的概念,如果不等待那么就会导致chk n的阶段处理了chk n+1阶段的数据,但是在source端所记录的消费偏移量又一致,如果chk n成功之后,后续的任务处理失败,任务重启会消费chk n+1阶段数据,就会到致数据重复消息,如果barrier等待就不会出现这样情况,因此barrier需要对齐那么就是实现exectly once语义,否则实现的是at least once语义。由于状态是属于flink内部存储,所以flink 仅仅满足内部exectly once语义。
两阶段提交
在分布式的系统中,我们采用两阶段提交协议来实现数据的一致性,两阶段提交就是把提交操作分为两个步骤,一个是预提交阶段,一个是提交阶段,通常这里包含两个角色,一个是master为协调管理者,一个是slave为执行者,提交过程如下:
1、master发出预提交命令给所有的slave
2、slave执行预提交的命令,执行完后给master发送一个ack反馈信息
3、当master收到所有的slave的成功反馈信息,那么再次给slave发送提交信息commit
4、slave执行commit操作
如果在上面的流程中,在流程2上出现问题,也就是预提交出现问题,那么master会收到slave的失败反馈,这个时候master会让slave进行回滚操作,保证数据的一致性,但是在流程4中出现问题,那么就会造成数据的不一致性,这个时候我们可以采用3次提交技术或者其他的方式进行数据修正,来达到最终的一致性。
flink的两阶段提交
flink中两阶段提交是为了保证端到端的Exactly Once,主要依托checkpoint机制来实现,先看一下checkpoint的整体流程,
1. jobMaster 会周期性的发送执行checkpoint命令(start checkpoint);
2.当source端收到执行指令后会产生一条barrier消息插入到input消息队列中,当处理到barrier时会执行本地checkpoint, 并且会将barrier发送到下一个节点,当checkpoint完成之后会发送一条ack信息给job