定义
当数据流中的许多操作只查看一个每次事件(如事件解析器),但一些操作会跨多个事件的信息(如窗口操作),这些操作称为有状态。状态由一个任务维护,该任务除了维护状态之外,还用来计算某个结果的所有数据。
算子的状态
有些算子有些任务是没有状态的,如map操作,只跟输入数据有关。像窗口操作不管是增量窗口函数还是全窗口函数都要保持里面的信息的,一开始在窗口到达结束时间之前是不输出数据的,所以最后输出数据的时候,他的计算是要依赖之前的,全窗口可以认为是把所有数据都作为状态保存下来。增量聚合窗口来一个聚合一次要保存的是中间聚合状态。像ProcessFunction可以有状态也可以没有状态。
有无状态的区别
无状态流处理和有状态流处理,主要区别:
- 无状态流处理分别接收每条输入数据,根据最新输入的数据生成输出数据;
- 有状态流处理会维护状态,
根据每条输入记录进行更新,并基于最新输入的记录和当前的状态值生成输出记录,即综合考虑多个事件之后的结果。
状态分类
- Keyed State: 只在KeyedStream后使用,键控状态总是相对于键,根据键来维护和访问的。
- Operator State :绑定到一个并行算子实例的状态,叫操作状态
- Kafka 中指定的Topic partition及groupId对应的offset
- Broadcast State:Operator State 的特殊情况
Key Stated
定义
Keyed State是指提供一个接口,用来访问作用到当前输入元素上
的不同类型的状态,由stream.keyBy(...)创建的keyed state仅可用于一个KeyStream里。
使用: 需要在DataStream指定一个Key:keyBy(keyselector)
分类
ValueState<T>
:保存单个的值
scss
ValueState.update(value: T)
ValueState.value()
ListState<T>
:保存集合元素
scss
ListState.add(value: T)、ListState.addAll(values: java.util.List[T])、ListState.update(values: java.util.List[T])
ListState.get()(注意:返回的是Iterable[T])
ReducingState<T>
保留一个值,该值表示添加到状态的所有值的汇总,需要用户提供ReduceFunction。
csharp
ReducingState.add(value: T)
ReducingState.get()
Aggregting State<IN,OUT>
保留一个值,该值表示添加到状态的所有值的汇总,需要用户提供AggregateFunction。
csharp
AggregatingState.add(value: T)
AggregatingState.get()
MapState<UK, UV>: 保存Key-Value对。
less
MapState.get(key: K)
MapState.put(key: K, value: V)
MapState.contains(key: K)
MapState.remove(key: K)
- 每个状态都有clear()是清空操作。
- Keyed State只能在KeyedStream后使用
- 键控状态总是相对于键,根据键来维护和访问的
- 状态并不一定存储在内存里,也有可能存储在磁盘上或其他位置上。
- 从状态中的值依赖于输入的数据
状态后端
Flink提供不同的State Backends状态后端,指定如何和在何处存储状态。
- MemoryStateBackend 将键控状态作为内存中的对象进行管理,将它们存储在TaskManager的JVM堆上,将checkpoint存储在JobManager的内存中
- FsStateBackend 本地状态存在TaskManager的JVM堆上,checkpoint存到远程的持久化文件系统(FileSystem)上
- RocksDBStateBackend 将所有状态序列化后,存入本地的RocksDB中存储。
状态描述器
定义
对状态的操作,需要创建一个叫StateDescriptor
,每一种State都有一个对应的StateDescriptor
,StateDescriptor包含状态名字、状态类型。举例:
1. 定义状态描述器
java
public class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>,uple2<Long,Long>> {
/**The ValueState handle. The first field is the count, the second field a running sum.
定以状本描述哭**/
//定义状态描述器
private transtent rawestateerrste ong, Long>> sum;
@Override
public void flatMap(Tuple2<Long, Long> input, Collector<Tuple2<Long,, Long>> 0ut) throws Exception {
// access the state value
Tuple2<Long,Long> currentSum = sum.value();
// update the count
2. 建获取状态描述器
java
@Override
public void open(Configuration config) {
ValueStateDescriptor<Tuple2<Long,Long>> descriptor =new ValueStateDescriptor<>(
"average",// the state name
//`<Tuple2<Long,Long>>`为状态类型,`average`为状态名称
TypeInformation.of(new TypeHint<Tuple2<Long, Long>>(){},//type information
Tuple2.of(0L,@L)); // default value of the state, if nothinq waste
//得到状态描述器
sum = getRuntimeContext().getState(descriptor);
状态描述器与状态对应关系
makefile
ValueState<T>:ValueStateDescriptor
MapSatte: MapStateDescriptor
ListSatte: ListStateDescriptor
ReduceSatte: ReduceStateDescriptor
...
获取状态描述器的方法
- 通过RichFunction先获取RuntimeContext
- 再通过
RuntimeContext.getState(...)
获取具体的描述器
各种状态的描述器获取方法
scss
ValueState<T> getState(ValueStateDescriptor<T>)
ReducingState<T> getReducingState(ReducingStateDescriptor<T>)
ListState<T> getListState(ListStateDescriptor<T>)
AggregatingState<IN,OUT> getAggregatingState(AggregatingStateDescriptor<IN,ACC,OUT>)MapState<UK,UV>
getMapState(MapStateDescriptor<UK,UV>)
状态描述器的生命周期:State Time-to-Live(TTL)
java
import org.apache.flink.api.common.state,StateTtiConfig;
import org.apache.flink,api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.time.Time;
StateTtlConfig ttlConfig = StateTtlConfig
.newBuilder(Time.seconds(1))
.setUpdateType(StateTtlConfig.UpdateType,OnCreateAndwrite)
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
.build();
ValueStateDescriptor<5tring> stateDescriptor = new ValueStateDescriptor<>("text state",String.class);
stateDescriptor.enableTimeToLive(ttlConfig);
OperatorState
OperatorState不是一种操作,而是与并行操作实例有关的状态。典型方法是KafkaConnector
:每一个并行的kafka消费实例都会维护一个包括topic partition
与offset
的OperatorState
。
使用方法
CheckpointedFunction
CheckpointedFunction接口通过不同的重新分发方案提供对非键控状态的访问。它需要实现如下两个方法:
java
void snapshotState(FunctionSnapshotContext context) throws Exception;
void initializeState(FunctionInitializationContext context) throws.Exception#