一、Keyed State & Operator State
### --- state 分类
~~~ # Operator State (或者non-keyed state )
~~~ 每个 Operator state 绑定一个并行 Operator 实例。
~~~ Kafka Connector 是使用 Operator state 的典型示例:
~~~ 每个并行的 kafka consumer 实例维护了每个 kafka topic 分区和该分区 offset 的映射关系,
~~~ 并将这个映射关系保存为 Operator state。
~~~ 在算子并行度改变时,Operator State 也会重新分配。
~~~ # Keyed State
~~~ 这种 State 只存在于 KeyedStream 上的函数和操作中,
~~~ 比如 Keyed UDF(KeyedProcessFunction…) window state 。
~~~ 可以把 Keyed State 想象成被分区的 Operator State。
~~~ 每个 Keyed State 在逻辑上可以看成与一个 <parallel-Operator-instance, key> 绑定,
~~~ 由于一个 key 肯定只存在于一个 Operator 实例,
~~~ 所以我们可以简单的认为一个 <operaor, key> 对应一个 Keyed State。
### --- 每个 Keyed State 在逻辑上还会被分配到一个 Key Group。分配方法如下:
~~~ # maxParallelism 为最大并行度
MathUtils.murmurHash(key.hashCode()) % maxParallelism;
~~~ 其中 maxParallelism 是 flink 程序的最大并行度,这个值一般我们不会去手动设置,
~~~ 使用默认的值(128)就好,这里注意下,
~~~ maxParallelism 和我们运行程序时指定的算子并行度(parallelism)不同,
~~~ parallelism 不能大于 maxParallelism ,parallelism 最多只能设置为 maxParallelism 。
~~~ # 为什么会有 Key Group 这个概念呢?
~~~ 举个栗子,我们通常写程序,会给算子指定一个并行度,运行一段时间后,
~~~ 积累了一些 state ,这时候数据量大了,需要增大并行度;
~~~ 我们修改并行度后重新提交,那这些已经存在的 state 该如何分配到各个 Operator 呢?
~~~ 这就有了最大并行度(maxParallelism ) 和 Key Group 的概念。
~~~ 上面计算 Key Group 的公式也说明了 Key Group 的个数最多是 maxParallelism 个。
### --- 当并行度更改后,我们再计算这个 key 被分配到的 Operator:
~~~ 可以看到, 一个 keyGroupId 会对应到一个 Operator,当并行度更改时,
~~~ 新的 Operator 会去拉取对应 Key Group 的 Keyed State,
~~~ 这样就把 KeyedState 尽量均匀地分配给所有的 Operator 啦!
keyGroupId * parallelism / maxParallelism;
### --- 根据 state 数据是否被 flink 托管,flink 又将 state 分类为 managed state 和 raw state:
~~~ managed state: 被 flink 托管,保存为内部的哈希表或者 RocksDB; checkpoint 时,
~~~ flink 将 state进行序列化编码。例如 ValueState ListState…
~~~ raw state: Operator 自行管理的数据结构,checkpoint 时,它们只能以 byte 数组写入checkpoint。
~~~ 当然建议使用 managed state 啦!使用 managed state 时,
~~~ flink 会帮我们在更改并行度时重新分发state,并且优化内存。
二、使用 managed keyed state
### --- 如何创建
~~~ # 上面提到,Keyed state 只能在 keyedStream 上使用,可以通过 stream.keyBy(…) 创建keyedStream。
~~~ # 我们可以创建以下几种 keyed state:
ValueState
ListState
ReducingState
AggregatingState<IN, OUT>
MapState<UK, UV>
FoldingState<T, ACC>
~~~ # 每种 state 都对应各自的描述符,通过描述符从 RuntimeContext 中获取对应的 State,
~~~ # 而RuntimeContext 只有 RichFunction 才能获取,所以要想使用 keyed state,
~~~ # 用户编写的类必须继承RichFunction 或者其子类。
ValueState getState(ValueStateDescriptor)
ReducingState getReducingState(ReducingStateDescriptor)
ListState getListState(ListStateDescriptor)
AggregatingState<IN, OUT> getAggregatingState(AggregatingStateDescriptor<IN, ACC, OUT>)
FoldingState<T, ACC> getFoldingState(FoldingStateDescriptor<T, ACC>)
MapState<UK, UV> getMapState(MapStateDescriptor<UK, UV>)
### --- 给 keyed state 设置过期时间
~~~ flink-1.6.0 以后,我们还可以给 Keyed state 设置 TTL(Time-To-Live),
~~~ 当某一个 key 的 state 数据过期时,会被 statebackend 尽力删除。
~~~ 官