Flink到底能不能实现exactly-once语义

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介: 关于这个问题其实从一开始很多人是存在质疑的,首先exactly-once语义指的是即使在出现故障的情况下,Flink流应用程序中的所有算子都保证事件只会被"精确一次"(恰好一次,不多不少)的处理.假设有下面一个场景,Flink在完成了一次checkpoint后,第二次checkpoint前(此时两个checkpoint中间的数据已经处理了一部分了)任务挂掉了,然后任务恢复的时候会从上一次成功的checkpoint处恢复(也即是checkpoint ID为1的位置)任务,那这个时候刚才被处理的数据又会被处理一次,这部分数据被处理了两次甚至可能是多次,那这就不能称为exactly-once语义了啊

关于这个问题其实从一开始很多人是存在质疑的,首先exactly-once语义指的是即使在出现故障的情况下,Flink流应用程序中的所有算子都保证事件只会被"精确一次"(恰好一次,不多不少)的处理.假设有下面一个场景,Flink在完成了一次checkpoint后,第二次checkpoint前(此时两个checkpoint中间的数据已经处理了一部分了)任务挂掉了,然后任务恢复的时候会从上一次成功的checkpoint处恢复(也即是checkpoint ID为1的位置)任务,那这个时候刚才被处理的数据又会被处理一次,这部分数据被处理了两次甚至可能是多次,那这就不能称为exactly-once语义了啊,这就是很多人存在疑惑的地方,这里先说一下结论:Flink是支持exactly-once语义的,但其针对的是Flink应用内部的数据流处理(也就是Flink的状态state来说的),换句话说,事件的处理可以发生多次,但是该处理的结果只在持久化后端状态存储中反映一次,Flink自身是无法保证端到端的exactly-once语义的.


Flink的失败恢复依赖于 检查点机制 + 可部分重放的数据源。


1,可重放的数据源这个很好理解,比如上游数据源是kafka,kafka是支持从指定的offest处重新开始消费数据的.


2,检查点机制是Flink的checkpoint定期触发,产生快照,快照里面记录了,


(1),当前检查点开始时数据源(例如Kafka)中消息的offset。


      (2),记录了所有有状态的operator当前的状态信息(例如sum中的数值)。


Flink是怎么保证容错恢复的时候保证数据没有丢失也没有数据的冗余呢?checkpoint是使Flink 能从故障恢复的一种内部机制。检查点是 Flink 应用状态的一个一致性副本,包括了输入的读取offest。在发生故障时,Flink 通过从检查点加载应用程序状态来恢复,并从恢复的offest位置继续处理,就好像什么事情都没发生一样。Flink的状态存储在Flink的内部,这样做的好处就是不再依赖外部系统,降低了对外部系统的依赖,在Flink的内部,通过自身的进程去访问状态变量.同时会定期的做checkpoint持久化,把checkpoint存储在一个分布式的持久化系统中,如果发生故障,就会从最近的一次checkpoint中将整个流的状态进行恢复.


Flink提供了Exactly once特性,是依赖于带有barrier的分布式快照+可部分重发的数据源功能实现的。而分布式快照中,就保存了operator的状态信息。


快照的核心概念之一是barrier。这些barrier被注入数据流并与记录一起作为数据流的一部分向下流动。barriers永远不会超过记录,数据流严格有序,barrier将数据流中的记录隔离成一系列的记录集合,并将一些集合中的数据加入到当前的快照中,而另一些数据加入到下一个快照中。


 每个barrier都带有快照的ID,并且barrier之前的记录都进入了该快照。barriers不会中断流处理,非常轻量级。来自不同快照的多个barrier可以同时在流中出现,这意味着多个快照可能并发地发生。


单流的barrier:



barrier在数据流源处被注入并行数据流中。快照n的barriers被插入的位置(记之为Sn)是快照所包含的数据在数据源中最大位置。例如,在Apache Kafka中,此位置将是分区中最后一条记录的偏移量。将该位置Sn报告给checkpoint协调器(Flink的JobManager)。


 然后barriers向下游流动。当一个中间操作算子从其所有输入流中收到快照n的barriers时,它会为快照n发出barriers进入其所有输出流中。一旦sink操作算子(流式DAG的末端)从其所有输入流接收到barriers n,它就向checkpoint协调器确认快照n完成。在所有sink确认快照后,意味着快照已完成。


 一旦完成快照n,job将永远不再向数据源请求Sn之前的记录,因为此时这些记录(及其后续记录)将已经通过整个数据流拓扑,也即是已经被处理结束。


简单来说barrier就是标识数据是属于哪个checkpoint的,把不同时间的数据划分到了不同的checkpoint里,比如上图的checkpoint barrier n-1 到checkpoint barrier n中间的数据是属于checkpoint n的.


多流的barrier:


网络异常,图片无法展示
|


接收多个输入流的运算符需要基于快照barriers上对齐(align)输入流。上图说明了这一点:


1,一旦操作算子从一个输入流接收到快照barriers n,它就不能处理来自该流的任何记录,直到它从其他输入接收到barriers n为止。否则,它会搞混属于快照n的记录和属于快照n + 1的记录。


2,barriers n所属的流暂时会被搁置。从这些流接收的记录不会被处理,而是放入输入缓冲区。可以看到1,2,3会一直放在Input buffer,直到另一个输入流的快照到达Operator。


3,一旦从最后一个流接收到barriers n,操作算子就会发出所有挂起的向后传送的记录,然后自己发出快照n的barriers。


4,之后,它恢复处理来自所有输入流的记录,在处理来自流的记录之前优先处理来自输入缓冲区的记录。


这个是在任务有多个并行度并且设置的是exactly-once语义的时候才有的barrier对齐过程,其实barrier对齐也非常好理解,如果只有一个并行度,这个时候不存在对齐,因为只有自己,不需要和谁对齐,如果有多个并行度,假如不对齐,可能会出现某一个并行度已经处理到下一个checkpoint的数据了,这个时候state里面的结果也发生了改变,任务重启的时候,从上一次成功的checkpoint里记录的offest恢复,这个时候结果就会被多计算,就达不到exactly-once了,所以多个并行度的时候必须有对齐的过程,确保多个并行度处理的是同一个checkpoint的数据.这样才能保证数据的一致性.


理解了Flink实现exactly-once的原理,我们在来还原一下开头说的那个场景.


网络异常,图片无法展示
|


这就是Flink任务失败恢复的时候状态恢复的过程,为了简化刚才的问题,我们假设kafka的partition只有一个,并且数据中的key也只有一个.


任务在第一次checkpoint的时候,快照里面的状态是:


1, <1,<0,10>> checkpoint的ID是1,kafka的paritition 0 消费的offest是10.


2,operator的状态是<hello,10>(实际这里的结构是(keygroup+key+namespace,value)).


假设checkpoint1到checkpoint2中间有10条数据,任务消费到offest为12的时候,任务挂掉了,此时状态state里面的结果是<hello,12>.任务会从上一次成功的checkpoint1 开始恢复, kafka的offest还是从10开始,算子的状态是<hello,10>,这个时候数据虽然重复消费了一遍,但是结果是对的,因为operator的状态也恢复到之前的状态了.所以对于状态来说确实是exactly-once语义.


看到这里你就会明白Flink是怎么实现状态的exactly-once的,那Flink如何实现端到端的exactly-once呢?这个就需要sink端支持事务或者幂等,然后结合Flink的二段提交去实现.这里就不做讨论了,后面的文章会再详细分析.


总结:


在这篇文章中,主要消除大家对Flink支持exactly-once的误解,Flink的exactly-once不是指数据只消费一次,而是数据虽然可能处理多次,但是结果(算子的状态)精准一次.Flink端到端的exactly-once需要sink支持事务或者幂等,用二段提交的方式完成.


相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
相关文章
|
7月前
|
数据处理 Apache 流计算
Flink Watermark和时间语义
Flink Watermark和时间语义
73 2
|
7月前
|
Apache 流计算
【Flink】Flink的三种时间语义
【4月更文挑战第19天】【Flink】Flink的三种时间语义
|
4月前
|
Java 微服务 Spring
驾驭复杂性:Spring Cloud在微服务构建中的决胜法则
【8月更文挑战第31天】Spring Cloud是在Spring Framework基础上打造的微服务解决方案,提供服务发现、配置管理、消息路由等功能,适用于构建复杂的微服务架构。本文介绍如何利用Spring Cloud搭建微服务,包括Eureka服务发现、Config Server配置管理和Zuul API网关等组件的配置与使用。通过Spring Cloud,可实现快速开发、自动化配置,并提升系统的伸缩性和容错性,尽管仍需面对分布式事务等挑战,但其强大的社区支持有助于解决问题。
83 0
|
4月前
|
消息中间件 Java 数据处理
揭秘Apache Flink的Exactly-Once神技:如何在数据流海中确保每条信息精准无误,不丢不重?
【8月更文挑战第26天】Apache Flink 是一款先进的流处理框架,其核心特性 Exactly-Once 语义保证了数据处理的精准无误。尤其在金融及电商等高要求场景下,该特性极为关键。本文深入解析 Flink 如何实现 Exactly-Once 语义:通过状态管理确保中间结果可靠存储;利用一致的检查点机制定期保存状态快照;以及通过精确的状态恢复避免数据重复处理或丢失。最后,提供一个 Java 示例,展示如何计算用户访问次数,并确保 Exactly-Once 语义的应用。
111 0
|
7月前
|
数据处理 Apache 流计算
Flink Watermark和时间语义
Flink Watermark和时间语义
|
7月前
|
数据处理 Apache 流计算
【Flink】Exactly-Once的保证
【4月更文挑战第21天】【Flink】Exactly-Once的保证
|
7月前
|
Java 数据处理 分布式数据库
Flink中的Exactly-Once语义是什么?请解释其作用和实现原理。
Flink中的Exactly-Once语义是什么?请解释其作用和实现原理。
85 0
|
7月前
|
消息中间件 Kafka 程序员
Flink(九)【时间语义与水位线】
Flink(九)【时间语义与水位线】
|
7月前
|
流计算
Flink Exactly-Once
Flink Exactly-Once
51 0
|
存储 消息中间件 缓存
Flink Exactly-Once 投递实现浅析
5万人关注的大数据成神之路,不来了解一下吗? 5万人关注的大数据成神之路,真的不来了解一下吗? 5万人关注的大数据成神之路,确定真的不来了解一下吗? 随着近来越来越多的业务迁移到 Flink 上,对 Flink 作业的准确性要求也随之进一步提高,其中最为关键的是如何在不同业务场景下保证 exactly-once 的投递语义。
3474 0
下一篇
DataWorks