Flink状态一致性

状态一致性

当在分布式系统中引入状态时,自然也引入了一致性问题。一致性实际上是"正确性级别"的另一种说法,也就是说在成功处理故障并恢复之后得到的结果,与没有发生任何故障时得到的结果相比,前者到底有多正确?

一致性级别

一致性级别分为三个等级:

  • at-most-once(最多一次): 这其实是没有正确性保障的委婉说法——故障发生之后,计数结果可能丢失。
    • 当任务故障时,最简单的做法是什么都不干,既不恢复丢失的状态,也不重播丢失的数据。At-most-once 语义的含义是最多处理一次事件。
  • at-least-once(至少一次): 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是说,计数程序在发生故障后可能多算,绝不会少算。
    • 在大多数的真实应用场景,我们希望不丢失事件。这种类型的保障称为 at-least-once,意思是所有的事件都得到了处理,而一些事件还可能被处理多次。
  • exactly-once(精准一次): 这指的是系统保证在发生故障后得到的计数结果与正确值一致。
    • 恰好处理一次是最严格的保证,也是最难实现的。恰好处理一次语义不仅仅意味着没有事件丢失,还意味着针对每一个数据,内部状态仅仅更新一次。

曾经,at-least-once 非常流行。第一代流处理器(如 Storm 和 Samza)刚问世时只保证 at-least-once,原因有二。

  • 保证 exactly-once 的系统实现起来更复杂。这在基础架构层(决定什么代表正确,以及 exactly-once 的范围是什么)和实现层都很有挑战性。

  • 流处理系统的早期用户愿意接受框架的局限性,并在应用层想办法弥补(例如使应用程序具有幂等性,或者用批量计算层再做一遍计算)。

最先保证 exactly-once 的系统(Storm Trident 和 Spark Streaming)在性能和表现力、这两个方面付出了很大的代价。

为了保证 exactly-once,这些系统无法单独地对每条记录运用应用逻辑,而是同时处理多条(一批)记录,保证对每一批的处理要么全部成功,要么全部失败。这就导致在得到结果前,必须等待一批记录处理结束。

因此,用户经常不得不使用两个流处理框架(一个用来保证 exactly-once,另一个用来对每个元素做低延迟处理),结果使基础设施更加复杂。曾经,用户不得不在保证exactly-once 与获得低延迟和效率之间权衡利弊。Flink 避免了这种权衡。

Flink 的一个重大价值在于,它既保证了 exactly-once,也具有低延迟和高吞吐的处理能力。

从根本上说,Flink 通过使自身满足所有需求来避免权衡,它是业界的一次意义重大的技术飞跃。尽管这在外行看来很神奇,但是一旦了解,就会恍然大悟。

一致性检查点

  • Flink 使用了一种轻量级快照机制 —— 检查点(checkpoint)来保证 exactly-once 语义

  • 有状态流应用的一致检查点,其实就是:所有任务的状态,在某个时间点的一份拷贝(一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候。

  • 应用状态的一致检查点,是 Flink 故障恢复机制的核心

端到端状态一致性

目前我们看到的一致性保证都是由流处理器实现的,也就是说都是在 Flink 流处理器内部保证的;而在真实应用中,流处理应用除了流处理器以外还包含了数据源(例如 Kafka)和输出到持久化系统。端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件。具体可以划分如下:

  • 内部保证 —— 依赖 checkpoint

  • source 端 —— 需要外部源可重设数据的读取位置

  • sink 端 —— 需要保证从故障恢复时,数据不会重复写入外部系统

而对于 sink 端,又有两种具体的实现方式:幂等(Idempotent)写入和事务性(Transactional)写入。

  • 幂等写入

    • 所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。
  • 事务写入

    • 需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint真正完成的时候,才把所有对应的结果写入 sink 系统中。

对于事务性写入,具体又有两种实现方式:预写日志(WAL)和两阶段提交(2PC)。DataStream API 提供了 GenericWriteAheadSink 模板类和TwoPhaseCommitSinkFunction 接口,可以方便地实现这两种方式的事务性写入。

幂等写入

一个幂等操作无论执行多少次都会返回同样的结果。例如,重复的向hashmap中插入同样的key-value对就是幂等操作,因为头一次插入操作之后所有的插入操作都不会改变这个hashmap,因为hashmap已经包含这个key-value对。

在这里插入图片描述

事务写入

其思想是只将最近一次成功保存的检查点之前的计算结果写入到外部系统中去。具体说就是构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所有对应的结果写入 sink 系统中

这样就保证了在任务故障的情况下,端到端恰好处理一次语义。应用将被重置到最近一次的检查点,而在这个检查点之后并没有向外部系统发出任何计算结果。

通过只有当检查点保存完成以后再写入数据这种方法,事务性的方法将不会遭受幂等性写入所遭受的重播不一致的问题。

尽管如此,事务性写入却带来了延迟,因为只有在检查点完成以后,我们才能看到计算结果。

预写日志

预写日志事务写入把结果数据先当成状态保存,然后在收到 checkpoint 完成的通知时,一次性写入 sink 系统。因为简单易于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink 系统,都能用这种方式一次性搞定。

DataStream API 提供了一个模板类:GenericWriteAheadSink,来实现这种事务性 sink。

两阶段提交

两阶段提交对于每个 checkpoint,sink 任务会启动一个事务,并将接下来所有接收的数据添加到事务里,然后将这些数据写入外部 sink 系统,但不提交它们 —— 这时只是“预提交”。当它收到 checkpoint 完成的通知时,它才正式提交事务,实现结果的真正写入。

这种方式真正实现了 exactly-once,它需要一个提供事务支持的外部sink 系统。Flink 提供了 TwoPhaseCommitSinkFunction 接口。

两阶段提交虽然可靠性高,但是对外部sink系统有很多要求:

  • 外部 sink 系统必须提供事务支持,或者 sink 任务必须能够模拟外部系统上的事务

  • 在 checkpoint 的间隔期间里,必须能够开启一个事务并接受数据写入

  • 在收到 checkpoint 完成的通知之前,事务必须是“等待提交”的状态。

  • 在故障恢复的情况下,这可能需要一些时间。如果这个时候sink系统关闭事务(例如超时了),那么未提交的数据就会丢失

  • sink 任务必须能够在进程失败后恢复事务

  • 提交事务必须是幂等操作

不可重置的源可重置的源
任意 sinkat-most-onceat-least-once
幂等性sinkat-most-onceexactly-once(当从任务失败中恢复时,存在暂时的不一致性)
预写式日志sinkat-most-onceat-least-once
2PC sinkat-most-onceexactly-once

Flink+Kafka实现端到端状态一致性保障

Exactly-once 两阶段提交步骤
  • 第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入 kafka 分区日志但标记为未提交,这就是“预提交”

  • jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到barrier 的算子将状态存入状态后端,并通知 jobmanager

  • sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知 jobmanager, 并开启下一阶段的事务,用于提交下个检查点的数据

  • jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成

  • sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据

  • 外部kafka关闭事务,提交的数据可以正常消费了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒 暄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值