Flink的八股文里一定离不开一个知识点:flink的四大基石是什么?答:检查点、状态、时间、窗口
今天我们从状态(state)开始,捋一捋它是怎么工作的
RuntimeContext
先来看看Flink源码中自带的一个state使用案例,这是一个如何在keyedStream中使用RichMapFunction的例子(在RuntimeContext.class里
DataStream<MyType> stream = ...;
KeyedStream<MyType> keyedStream = stream.keyBy("id");
keyedStream.map(new RichMapFunction<MyType, List<MyType>>() {
private FoldingState<MyType, Long> state;
public void open(Configuration cfg) {
state = getRuntimeContext().getFoldingState(
new FoldingStateDescriptor<>("sum", 0L, (a, b) -> a.count() + b, Long.class));
}
public Tuple2<MyType, Long> map(MyType value) {
state.add(value);
return new Tuple2<>(value, state.get());
}
});
可以看到,这里在map方法中传入了一个RichMapFunction,然后重写了这个类里的一些方法,包括初始化方法open和处理数据的map方法,而这个state是从RuntimeContext中产生的。
RuntimeContext(官方注释):RC包含functions执行所需要的信息,每个function都有一个context来访问静态环境信息(如当前并行度)和其他结构(如累加器accumulator和广播变量)。
通过看RC这个接口的方法,我们可以看到,所有的state都可以通过RC新建出来
注意,这里有ValueState和KeyedState两种不一样的创建方法,这两种类型在后面多处都有体现
State的逻辑结构
state的逻辑结构由state接口定义,接口中只有一个clear方法,我们来看看它的继承关系
- ReadOnlyBroadcastState
- 提供只读的广播变量,kv类型
- BroadcastState
- 和上面类似,可以读写
- MapState
- ValueState
- 不区分key,只有一个值
- AppendingState
- 支持累加操作
- MergingState
- 在上面的基础上增加了合并功能,也就是两个MergingState合并成一个
- ListState
- 以数组存储,支持merge
- AggregatingState
- 支持定义aggregate方法
- ReducingState
- 支持reduce方法
- FlodingState
- 比较老的api,建议用aggregate替代
细心的读者可能发现了,上图中还有一类InternalXXXState,这个是flink内部使用的状态管理,相比给用户用的state,多了命名空间管理、kv序列化等方法
Internal的state都有一个共同的父接口InternalKvState(包括InternalValueState)
关于InternalXXXState接口的具体继承关系,flink官方在注释里画了个很生动的图
提供一些接口截图给大家结合着理解一下
以上是state接口部分
State的物理结构
如果说state接口定义了状态的数据结构,那么StateBackend定义了状态的物理存储
以下为翻译官方注释:
StateBackend:定义了流应用的state如何存储和做检查点。不同的StateBackend用不同的方法存储数据、用不同的数据结构来存储正在运行的应用的状态。
例如:
MemoryStateBackend:在TaskManager的内存中存储状态,checkpoint保存在JobManager中。这种backend很轻量,没有多余的依赖,但没有高可用且只支持较小的state。
FsStateBackend:在TaskManager的内存中存储状态,在文件系统中存checkpoint。
RocksDBStateBackend:在Rocks DB中存state,在文件系统中存checkpoint。
StateBackend为KeyedState和operatorState提供原生字节存储(raw bytes storage)
在以上三种定义了存储介质的Backend的基础上,每个类都提供了存储对应keyedstate和valuestate的backend,分别称为KeyedStateStateBackend和OperatorStateBackend
强调:上面三个类都有这两个方法
然后我们再来看看这KeyedStateStateBackend和OperatorStateBackend底层在如何存储state数据吧
KeyedStateStateBackend
以HeapKeyedStateBackend为例
可以看到StateTable和HPQSRW两种数据结构
StateTable:提供了基于key和namespace的state索引方法,类似map,但是需要两个k
HPQSRW:顾名思义,就是基于堆和优先级队列的存储set类型state的方式
OperatorStateBackend
这个接口下面只有DeafultOperatorStateBackend,直接看
可以看到明显分了两种存储方式,上面的是存普通operator ,下面是存广播算子的状态
PartitionableListState:,很简单,ArrayList写在脸上了
BackendWritableBroadcastState:同理,底层是map
以上,state的逻辑存储和物理存储都尽我所能解密完毕了