状态
状态的管理
在Flink中,算子任务可以分为无状态和有状态两种
无状态算子包括map、filter、flatMap等,有状态算子包括聚合算子、窗口算子等
有状态算子的一般处理流程:算子任务接收到上游发来的数据;获取当前状态;根据业务逻辑进行计算,更新状态;得到计算结果,输出发送到下游任务
Flink中状态作为内存变量可以直接交给JVM管理,但是在大数据场景下,利用分布式架构进行扩展后,需要解决以下问题
-
状态的访问权限
我们知道 Flink 上的聚合和窗口操作,一般都是基于 KeyedStream 的,数据会按照 key 的哈希值进行分区,聚合处理的结果也应该是只对当前 key 有效
然而同一个分区(也就是 slot)上执行的任务实例,可能会包含多个 key 的数据,它们同时访问和更改本地变量,就会导致计算结果错误。所以这时状态并不是单纯的本地变量
-
容错性
也就是故障后的恢复。状态只保存在内存中显然是不够稳定的,我们需要将它持久化保存,做一个备份;在发生故障后可以从这个备份中恢复状态
-
分布式应用的横向扩展性
比如处理的数据量增大时,我们应该相应地对计算资源扩容,调大并行度。这时就涉及到了状态的重组调整
Flink 有一套完整的状态管理机制,将底层一些核心功能全部封装起来,包括状态的高效存储和访问、持久化保存和故障恢复,以及资源扩展时的调整
状态的分类
1. 托管状态(Managed State)和原始状态(Raw State)
托管状态
托管状态就是由 Flink 统一管理的,状态的存储访问、故障恢复和重组等一系列问题都由 Flink 实现,我们只要调接口就可以;
具体来讲,托管状态是由 Flink 的运行时(Runtime)来托管的;在配置容错机制后,状态会自动持久化保存,并在发生故障时自动恢复。当应用发生横向扩展时,状态也会自动地重组分配到所有的子任务实例上
对于具体的状态内容,Flink 也提供了值状态(ValueState)、 列表状态(ListState)、映射状态(MapState)、聚合状态(AggregateState)等多种结构,内部 支持各种数据类型。聚合、窗口等算子中内置的状态,就都是托管状态;
我们也可以在富函数 类(RichFunction)中通过上下文来自定义状态,这些也都是托管状态
原始状态
而原始状态则是自定义的,相当于就是开辟了一块内存,需要我们自己管理,实现状态的序列化和故障恢复
2. 算子状态(Operator State)和按键分区状态(Keyed State)
算子状态
状态作用范围限定为当前的算子任务实例,也就是只对当前并行子任务实例有效
算子状态可以用在所有算子上,使用的时候其实就跟一个本地变量没什么区别,因为本地变量的作用域也是当前任务实例
在使用时,我们还需进一步实现 CheckpointedFunction 接 口
算子状态也支持不同的结构类型,主要有三种:ListState、UnionListState 和 BroadcastState
按键分区状态
状态是根据输入流中定义的键(key)来维护和访问的,所以只能定义在按键分区流 (KeyedStream)中,也就 keyBy 之后才可以使用
另外,也可以通过富函数类(Rich Function) 来自定义 Keyed State,所以只要提供了富函数类接口的算子,也都可以使用 Keyed State
无论是 Keyed State 还是 Operator State,它们都是在本地实例上维护的,也就是说每个并行子任务维护着对应的状态,算子的子任务之间状态不共享
因为一个并行子任务可能会处理多个 key 的数据,所以 Flink 需要对 Keyed State 进行一些特殊优化
在底层,Keyed State 类似于一个分布式的映射(map)数据结构,所有的状态会根据 key 保存成键值对(key-value)的形式。这样当一条数据到来时,任务就会自动将状态的访问范围限定为当前数据的 key,从 map 存储中读取出对应的状态值
所以具有相同 key 的所有数据都会到访问相同的状态,而不同 key 的状态之间是彼此隔离的
另外,在应用的并行度改变时,状态也需要随之进行重组。不同 key 对应的 Keyed State 可以进一步组成所谓的键组(key groups),每一组都对应着一个并行子任务。键组是 Flink 重 新分配 Keyed State 的单元,键组的数量就等于定义的最大并行度。当算子并行度发生改变时, Keyed State 就会按照当前的并行度重新平均分配,保证运行时各个子任务的负载相同
需要注意,使用 Keyed State 必须基于 KeyedStream。没有进行 keyBy 分区的 DataStream, 即使转换算子实现了对应的富函数类,也不能通过运行时上下文访问 Keyed State
状态持久化和状态后端
检查点
有状态流应用中的检查点(checkpoint),其实就是所有任务的状态在某个时间点的一个快照(一份拷贝)
状态后端
检查点的保存离不开 JobManager 和 TaskManager,以及外部存储系统的协调
在应用进行检查点保存时,首先会由 JobManager 向所有 TaskManager 发出触发检查点的命令; TaskManger 收到之后,将当前任务的所有状态进行快照保存,持久化到远程的存储介质中; 完成之后向 JobManager 返回确认信息。这个过程是分布式的,当 JobManger 收到所有 TaskManager 的返回信息后,就会确认当前检查点成功保存,而这一切工作的 协调,就需要一个“专职人员”来完成,这个组件就叫作状态后端(state backend)

状态后端主要负责两件事:一是本地的状态管理,二是将检查 点(checkpoint)写入远程的持久化存储

浙公网安备 33010602011771号