- 什么是状态?状态有什么作用?
- 如果你来设计,对于一个流式服务,如何根据不断输入的数据计算呢?
- 又如何做故障恢复呢?
一、为什么要管理状态
流计算不像批计算,数据是持续流入的,而不是一个确定的数据集。在进行计算的时候,不可能把之前已经输入的数据全都保存下来,然后再和新数据合并计算。效率低下不说,内存也扛不住。
另外,如果程序出现故障重启,没有之前计算过的状态保存,那么也就无法再继续计算了。
因此,就需要一个东西来记录各个算子之前已经计算过值的结果,当有新数据来的时候,直接在这个结果上计算更新。这个就是状态。
常见的流处理状态功能如下:
- 数据流中的数据有重复,我们想对重复数据去重,需要记录哪些数据已经流入过应用,当新数据流入时,根据已流入过的数据来判断去重。
- 检查输入流是否符合某个特定的模式,需要将之前流入的元素以状态的形式缓存下来。比如,判断一个温度传感器数据流中的温度是否在持续上升。
- 对一个时间窗口内的数据进行聚合分析,分析一个小时内某项指标的75分位或99分位的数值。
- 在线机器学习场景下,需要根据新流入数据不断更新机器学习的模型参数。
二、state 简介
Flink的状态是由算子的子任务来创建和管理的。一个状态更新和获取的流程如下图所示,一个算子子任务接收输入流,获取对应的状态,根据新的计算结果更新状态。
状态的保存:
需要考虑的问题:
- container 异常后,状态不丢
- 状态可能越来越大
因此,状态不能直接放在内存中,以上两点问题都无法保证。
需要有一个外部持久化存储方式,常见的如放到 HDFS 中。(此部分读者感兴趣可自行搜索资料探索)
三、Flink 状态类型
一)Managed State 和 Raw State
- Managed State 是由 Flink 管理的。Flink帮忙存储、恢复和优化。
- Raw State 是开发者自己管理的,需要自己序列化(较少用到)。
在 Flink 中推荐用户使用Managed State管理状态数据 ,主要原因是 Managed State 能够更好地支持状态数据的重平衡以及更加完善的内存管理。
Managed State | Raw State | |
---|---|---|
状态管理方式 | Flink Runtime 管理,自动存储,自动恢复,内存管理方式上优化明显 | 用户自己管理,需要用户自己序列化 |
状态数据结构 | 已知的数据结构 value , list ,map | flink不知道你存的是什么结构,都转换为二进制字节数据 |
使用场景 | 大多数场景适用 | 需要满足特殊业务,自定义operator时使用,flink满足不了你的需求时候,使用复杂 |
下文将重点介绍Managed State。
二)Keyed State 和 Operator State
Managed State 又有两种类型:Keyed State 和 Operator State。
keyed state | operator state | |
---|---|---|
适用场景 | 只能应用在 KeyedSteam 上 | 可以用于所有的算子 |
State 处理方式 | 每个 key 对应一个 state,一个 operator 处理多个 key ,会访问相应的多个 state | 一个 operator 对应一个 state |
并发改变 | 并发改变时,state随着key在实例间迁移 | 并发改变时需要你选择分配方式,内置:1.均匀分配 2.所有state合并后再分发给每个实例 |
访问方式 | 通过RuntimeContext访问,需要operator是一个richFunction | 需要你实现CheckPointedFunction或ListCheckPointed接口 |
支持数据结构 | ValuedState , ListState , Reducing State , Aggregating State , MapState , FoldingState(1.4弃用) |
只支持 listState |
Keyed State
简单来说,通过 keyBy 分组的就会用到 Keyed State。就是按照分组来的状态。(Keyed State 是Operator State的特例,区别在于 Keyed State 事先按照 key 对数据集进行了分区,每个 Key State 仅对应ー个Operator和 Key 的组合。)
Keyed State可以通过 Key Groups 进行管理,主要用于当算子并行度发生变化时,自动重新分布Keyed State数据 。分配代码如下:
// KeyGroupRangeAssignment.java
public