端到端的Exactly-Once问题是分布式系统领域最具挑战性的问题之一,很多框架都在试图攻克这个难题。
在这个问题上,Flink内部状态的一致性主要依赖Checkpoint机制,外部交互的一致性主要依赖Source和Sink提供的一些功能。Source需要支持重发功能,Sink需要采用一定的数据写入技术,比如幂等写或事务写。
对于Source重发功能,如上图所示,只要我们记录了输入的偏移量Offset,故障重启后数据发送方从该Offset重新开始发送数据即可。
Kafka的Producer除了发送数据,还会将数据持久化写到日志文件中。
如果下游应用重启,Producer根据下游提供的Offset,从持久化的文件中定位到数据,可以重新开始向下游发送数据。
Source的重发会导致一条数据被处理多次,为了保证只对下游系统产生一次影响,还需要依赖Sink的幂等写或事务写。下面重点介绍这这两个概念。
幂等写
幂等写(Idempotent Write)操作是指,任意多次向一个系统写入数据,只对目标系统产生一次结果影响。
例如,重复向一个HashMap
里插入同一个Key-Value二元对,第一次插入时这个HashMap
发生变化,后续的插入操作不会改变HashMap
的结果,这就是一个幂等写操作。重复地对一个整数执行加法操作就不是幂等写,因为多次操作后,这个整数会变大。
像Cassandra、HBase和Redis这样的KV数据库一般经常用来作为Sink,用以实现端到端的Exactly-Once。需要注意的是,并不是说一个KV数据库就百分百支持幂等写。幂等写对KV对有要求,那就是Key-Value必须是可确定性(Deterministic)计算的。假如我们设计的Key是:name + curTimestamp
,每次执行数据重发时,生成的Key都不相同,会产生多次结果,整个操作不是幂等的。因此,为了追求端到端的Exactly-Once,我们设计业务逻辑时要尽量使用确定性的计算逻辑和数据模型。
事务写
事务(Transaction)是数据库系统索要解决的最核心问题。Flink借鉴了数据库中的事务处理技术,同时结合自身的Checkpoint机制来保证Sink只对外部输出产生一次影响。
简单概括来说,Flink的事务写(Transaction Write)是指,Flink先将待输出的数据保存下来暂时不向外部系统提交,等待Checkpoint结束的时刻,Flink上下游所有算子的数据都是一致时,将之前保存的数据全部提交(Commit)到外部系统。
换句话说,只有经过Checkpoint确认的数据才向外部系统写入。
那么数据重发的例子中,入下图所示,如果使用事务写,那只把时间戳3之前的输出提交到外部系统,时间戳3以后的数据(例如时间戳5和8生成的数据)暂时保存下来,等待下次Checkpoint时一起写入到外部系统。
这就避免了时间戳5这个数据产生多次结果,多次写入到外部系统。
代码:
https://blog.csdn.net/zuochang_liu/article/details/106507609
https://blog.csdn.net/weixin_43947279/article/details/109995624
https://blog.csdn.net/weixin_43283487/article/details/107128683