Flink中状态编程和容错机制

流式计算分为无状态和有状态两种情况;无状态的计算观察每个独立事件,并根据最后一个事件输出结果;有状态的计算则会基于多个事件输出结果

Flink状态管理包含哪些?

状态一致性,故障处理以及高效存储和访问,以便开发人员可以专注于应用程序的逻辑

1、状态的类型

在Flink中,状态始终与特定算子相关联,总的来说,有两种类型的状态:

  1. 算子状态(Operator State)
  2. 键控状态(Keyed State)

1.1 算子状态(Operator State)

算子状态的作用范围限定为算子任务,由同一并行任务所处理的所有数据都可以访问到相同的状态,状态对于同一任务而言是共享的,算子状态不能由相同或不同算子的另一个任务访问。

Flink为算子状态提供三种基本数据结构

  1. 列表状态(List state)

    将状态表示为一组数据的列表

  2. 联合列表状态(Union list state)

    也将状态表示为数据的列表;它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复

  3. 广播状态(Broadcast state)

    如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态

1.2 键控状态(Keyed State)

键控状态是根据输入数据流中定义的键(key)来维护和访问的;Flink为每个key维护一一个状态实例,并将具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个key对应的状态;当任务处理一条数据时, 它会自动将状态的访问范围限定为当前数据的key;因此,具有相同key的所有数据都会访问相同的状态。

Flink的Keyed State支持以下数据类型

  1. 值状态(Value state)

    将状态表示为单个的值

  2. 列表状态(List state)

    将状态表示为一组数据的列表

  3. 映射状态(Map state)

    将状态表示为一-组Key-Value对

  4. 聚合状态(Reducing state & Aggregating State)

    将状态表示为一个用于聚合操作的列表

2、 状态一致性

一致性实际上是“正确性级别”的另一种说法

有状态的流处理,内部每个算子任务都可以有自己的状态

对于流处理器内部来说,所谓的状态一致性, 其实就是我们所说的计算结果要保证准确

一条数据不应该丢失,也不应该重复计算

在遇到故障时可以恢复状态,恢复以后的重新计算,结果应该也是完全正确的

2.1 一致性级别

  1. at-most-once

    这其实是没有正确性保障的委婉说法,故障发生之后,计数结果可能丢失

  2. at-least-once

    这表示计数结果可能大于正确值,但绝对不会小于正确值;也就是说,计数程序在发生故障后可能多算,但是绝不会少算

  3. exactly-once
    这指的是系统保证在发生故障后得到的计数结果与正确值一致

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

2.2 端到端(end-to-end)状态一致性

目前我们看到的一致性保证都是由流处理器实现的,也就是说都是在 Flink 流处理器内部保证的;而在真实应用中,流处理应用除了流处理器以外还包含了数据源(例如Kafka)和输出到持久化系统

端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件

端到端(end-to-end)状态一致性具体可以划分如下:

  1. 内部保证:依赖checkpoint

  2. source端:需要外部源可重设数据的读取位置

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

    sink端有哪两种具体的实现方式?

    • 幂等写入

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

    • 事务写入

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

      事务写入有两种实现方式

      • 预写日志(WAL)
      • 两阶段提交(2PC)

不同source和sink的一致性保证可以用下表说明:

sink\source不可重置可重置
任意(Any)at-most-onceat-least-once
幂等at-most-onceexactly-once(故障恢复时会出现暂时不一致)
预写日志(WAL)at-most-onceat-least-once
两阶段提交(2PC)at-most-onceexactly-once

3、检查点(checkpoint)

Flink具体如何保证exactly-once呢?它使用一种被称为“检查点”(checkpoint)的特性,在出现故障时将系统重置回正确状态

Flink检查点的核心作用时确保状态正确,集市遇到程序中断,也要正确

检查点分割线(checkpoint barriers)和普通数据记录类似,它们由算子处理,但并参与计算,而是会触发与检查点相关的行为

检查点是Flink最有价值的创新之一,因为它使Flink可以保证exactly-once,并且不需要牺牲性能

3.1 如何从检查点恢复状态?

在执行流应用程序期间,Flink会定期保存状态一致的检查点,如果发生故障,Flink将会使用最近的检查点来一致恢复应用程序的状态,并重新启动处理流程,遇到故障之后

第一步:重启应用

第二步:从checkpoint中读取状态,将状态重置,从检查点重新启动应用程序后,其内部状态与检查点完成时的状态完全相同

第三步:开始消费并处理检查点到发生故障之间的所有数据,这种检查点的保存和恢复机制可以为应用程序状态提供“精确一次”(exactly-once)的一致性,因为所有算子都会保存检查点并恢复其所有状态,这样一来所有的输入流就都会被重置到检查点完成时的位置

3.2 保存点(savepoint)

Flink还提供了可以自定义的镜像保存功能,就是保存点(savepoint)

原则上,创建保存点使用的算法与检查点完全相同,因此保存点可以认为就是具有一些额外元数据的检查点

Flink不会自动创建保存点,因此用户(或者外部调度程序)必须明确地触发创建操作

保存点是一一个强大的功能,除了故障恢复外,保存点可以用于:有计划的手动备份,更新应用程序,版本迁移,暂停和重启应用等等

3.3 检查点(checkpoint)和保存点(savepoint)的区别?

  1. checkpoint的侧重点是容错,即Flink作业意外失败并重启之后,能够直接从早先打下的checkpoint恢复运行,且不影响作业逻辑的准确性

    savepoint的侧重点是维护,即Flink作业需要在人工干预下手动重启、升级、迁移或A/B测试时,先将状态整体写入可靠存储,维护完毕之后再从savepoint恢复现场

  2. savepoint是通过checkpoint机制创建的,所以savepoint本质上是特殊的checkpoint

  3. checkpoint面向Flink Runtime本身,由Flink的各个TaskManager定时触发快照并自动清理,一般不需要用户干预

    savepoint面向用户,完全根据用户的需要触发与清理。

  4. checkpoint的频率往往比较(因为需要尽可能保证作业恢复的准确度),所以checkpoint的存储格式非常轻量级,但作为trade-off牺牲了一切可移植(portable)的东西,比如不保证改变并行度和升级的兼容性

    savepoint则以二进制形式存储所有状态数据和元数据,执行起来比较而且,但是能够保证portability,如并行度改变或代码升级之后,仍然能正常恢复

  5. checkpoint是支持增量的(通过RocksDB),特别是对于超大状态的作业而言可以降低写入成本

    savepoint并不会连续自动触发,所以savepoint没有必要支持增量

4、Flink + Kafka如何实现端到端的exactly-once语义

我们知道,端到端的状态一致性的实现,需要每一个组件都实现,对于 Flink + Kafka 的数据管道系统(Kafka 进、Kafka 出)而言,各组件怎样保证exactly-once语义呢?

  1. 内部:利用checkpoint机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
  2. source:kafka consumer作为source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重置偏移量,重新消费数据,保证一致性
  3. sink:kafka producer作为sink,采用两阶段提交sink,需要实现一个TwoPhaseCommitSinkFunction

kafka精确一致性(Exactly-once ) 两阶段提交的步骤?

  1. 第一条数据来了之后,开启一个kafka的事务(transaction),正常写入kafka分区日志但标记为未提交,这就是“预提交”
  2. JobManager触发checkpoint操作,barrier从source开始向下传递,遇到barrier的算子将状态存入状态后端,并通知JobManager
  3. sink连接器收到barrier,保存当前状态,存入checkpoint,通知JobManager,并开启下一阶段的事务,用于提交下个检查点的数据
  4. JobManager收到所有任务的通知,发出确认信息,表示checkpoint完成
  5. sink任务收到JobManager的确认信息,正式提交这段时间的数据
  6. 外部kafka关闭事务,提交的数据可以正常消费了

5、状态后端(state backend)

5.1 什么是状态后端?

每传入一条数据,有状态的算子任务都会读取和更新状态

由于有效的状态访问对于处理数据的低延迟至关重要,因此每个并行任务都会在本地维护其状态,以确保快速的状态访问

状态的存储、访问以及维护,由一个可插入的组件决定,这个组件就叫做状态后端(state backend)

5.2 后端的职责

  1. 本地的状态管理

  2. 将检查点(checkpoint)状态写入远程存储

5.3 状态后端的类型

  1. MemoryStateBackend

    • 内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在TaskManager的JVM堆上,而将checkpoint存储在JobManager的内存中
    • 特点:快速、低延迟,但不稳定
  2. FsStateBackend

    • 将checkpoint存到远程的持久化文件系统(FileSystem)上,而对于本地状态,跟MemoryStateBackend一 样,也会存在TaskManager的JVM堆上
    • 同时拥有内存级的本地访问速度,和更好的容错保证
  3. RocksDBStateBackend

    • 将所有状态序列化后,存入本地的RocksDB中存储
    • 注意:RocksDB的支持并不直接包含在flink中,需要引入依赖
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值