——wirte by 橙心橙意橙续缘,
前言
白话系列
————————————————————————————
也就是我在写作时完全不考虑写作方面的约束,完全把自己学到的东西、以及理由和所思考的东西等等都用大白话诉说出来,这样能够让信息最大化的从自己脑子里输出并且输入到有需要的同学的脑中。PS:较为专业的地方还是会用专业口语诉说,大家放心!
白话Flink系列
————————————————————————————
主要是记录本人(国内某985研究生)在Flink基础理论阶段学习的一些所学,更重要的是一些所思所想,所参考的视频资料或者博客以及文献资料均在文末放出.由于研究生期间的课题组和研究方向与Flink接轨较多,而且Flink的学习对于想进入大厂的同学们来说也是非常的赞,所以该系列文章会随着本人学习的深入来不断修改和完善,希望大家也可以多批评指正或者提出宝贵建议。
初识状态Stated
—————————————————————————————
Apache Flink 是一个框架和分布式处理引擎,用于对无边界和有界数据流进行有状态计算。这是Flink官网的定义,Flink中的计算需要利用状态来进行。
利用内存性能
有状态 Flink 应用程序针对本地状态访问进行了优化。任务状态始终保留在内存中,或者,如果状态大小超过可用内存
,则在访问效率高的磁盘数据结构
中。因此,任务通过访问本地(通常为内存中)状态来执行所有计算,从而产生非常低的处理延迟。Flink 通过定期和异步检查本地状态到持久存储,在发生故障时保证一次状态一致性
。
Flink官网对状态管理的介绍,大家先粗略的了解一下。
Flink中的状态
- 状态State通俗理解就是
中间结果以及中间变量
,它保存在执行该Task的机器本地。但是该变量要向Flink系统注册
,这样的话出现故障,Flink状态管理系统才能恢复它出错前的结果。 - 上图可以看到,对于每个Task都有属于自己的状态State,而且这个状态会被不断地
更新
,所以状态就需要可被读取,也就是可被业务逻辑所访问
举个例子,想reduce,sum,maxBy这样的聚合操作就包含有状态来保存上次运行后的结果,而像map、fliter这样的算子就不包含状态[但不代表它们不能拥有状态]。
- managered State 被Flink集群所管理的状态,不用人为操作。
- raw State 不被Flink集群所管理的状态,需要在底层手动实现。
Operator State 算子状态
- 始终与特定算子相关联的状态。
- 算子状态要想被Flink管理,首现要向Flink
注册
其状态 - 算子状态的作用范围限定为算子任务
图中该算子包含两个并行的子任务,每个子任务对应1个分区,所以说每个分区内的数据可获得的算子状态是相同的。
算子状态的数据结构
-
列表状态 List state
状态是一个列表,也就是一组数据。 -
联合列表状态 Union list state
状态也是一个列表,它和列表状态的区别在于发生故障时/或者从保存点恢复时的方式不同
由图可知,列表状态的重分配是均分,而联合列表状态是把所有状态都分配过去,让算子自己选择需要的。 -
广播状态 Broadcast state
每个算子所对应task的不同subtask的状态全部相同,即使在不同分区。
// 自定义MapFunction
public static class MyCountMapper implements MapFunction<SensorReading, Integer>, ListCheckpointed<Integer>{
// 定义一个本地变量,作为算子状态
private Integer count = 0;
@Override
public Integer map(SensorReading value) throws Exception {
count++;
return count;
}
@Override
public List<Integer> snapshotState(long checkpointId, long timestamp) throws Exception {
return Collections.singletonList(count);
}
@Override
public void restoreState(List<Integer> state) throws Exception {
for( Integer num: state )
count += num;
}
}
普通的map算子通过继承
ListCheckpointed接口
来实现对状态
即本地变量count
的管理,具体来说就是实现了接口中的snapshotState
函数(状态保存快照)和restoreState
(出错恢复状态)函数来进行。【这里采用的是Flink列表状态,只不过我们定义的状态是个整形数据而已,不冲突。】
Keyed Stated 键控状态
- 根据输入数据流中定义的Key来维护和访问。
- 在此之前首现要进行keyBy()或者按照HashCode进行重分区的操作。
- 每个subtask也就是一个分区都会含有多个key值的数据,那么每个数据只能访问自己key值对应的状态。(如下图不同颜色所示)
- Value State 值状态 —— 状态为单个值
- List State 列表状态 —— 状态为1个List
- Map State 映射状态 —— 状态为1组Map对
- Reducing state & Aggregating state 聚合状态 —— 状态为用于聚合操作的List
比较简单,都比较直白。
// 自定义RichMapFunction
public static class MyKeyCountMapper extends RichMapFunction<SensorReading, Integer>{
private ValueState<Integer> keyCountState;
// 其它类型状态的声明
private ListState<String> myListState;
private MapState<String, Double> myMapState;
private ReducingState<SensorReading> myReducingState;
@Override
public void open(Configuration parameters) throws Exception {
keyCountState = getRuntimeContext().getState(new ValueStateDescriptor<Integer>("key-count", Integer.class, 0));
myListState = getRuntimeContext().getListState(new ListStateDescriptor<String>("my-list", String.class));
myMapState = getRuntimeContext().getMapState(new MapStateDescriptor<String, Double>("my-map", String.class, Double.class));
// myReducingState = getRuntimeContext().getReducingState(new ReducingStateDescriptor<SensorReading>())
}
@Override
public Integer map(SensorReading value) throws Exception {
// 其它状态API调用
// list state
for(String str: myListState.get()){
System.out.println(str);
}
myListState.add("hello");
// map state
myMapState.get("1");
myMapState.put("2", 12.3);
myMapState.remove("2");
// reducing state
// myReducingState.add(value);
myMapState.clear();
Integer count = keyCountState.value();
count++;
keyCountState.update(count);
return count;
}
}
代码
private ValueState<Integer> keyCountState = getRuntimeContext().getState(new ValueStateDescriptor<Integer>("key-count", Integer.class, 0));
private ListState<String> myListState = getRuntimeContext().getListState(new ListStateDescriptor<String>("my-list", String.class));
private MapState<String,Double> myMapState = getRuntimeContext().getMapState(new MapStateDescriptor<String, Double>("my-map", String.class, Double.class));
private ReducingState<SensorReading> myReducingState = getRuntimeContext().getReducingState(new ReducingStateDescriptor<SensorReading>()) //这里传入的状态值的数据类型就是数据本身的类型
- 可以看到keyed Sate 需要通过
getRuntimeContext()
来进行分配。对于上述不同的键控状态都有相应的状态分配器来进行分配。 - 而且由于要用到上下文信息,所以通过继承
RichMapFunction
接口来进行自定义map函数。 - 由于上下文环境必须等我们的算子生命周期开始才能够获得,所以说键控状态的分配必须要在
Open
函数中进行实现才可以。 - 对于不同的键控状态我们可以调用它自带的API来进行状态值的读取和更新、清空等操作。
状态后端 State Backends
状态的存储、访问以及维护,由一个可插入
的组件
决定,这个组件就叫做状态后端。
- 本地的状态管理,读、写、更新等等。
- 将检查点CheckPoint状态写入远程存储(做快照)。
Flink中提供有3中状态后端。
- MemoryStateBackend
- 内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储 在 TaskManager 的 JVM 堆上;而将checkpoint 存储在 JobManager 的内存中。
- 特点:快速、低延迟、但不稳定
- FsStateBackend
- 将 checkpoint 存到远程的持久化文件系统(FileSystem)上。而对于本地状 态,跟 MemoryStateBackend 一样,也会存在 TaskManager 的 JVM 堆上。
- 同时拥有内存级的本地访问速度,还有更好的容错保证。
- RocksDBStateBackend
- 将所有状态序列化后,存入本地的 RocksDB ((内存磁盘混合的 LSM DB))中存储。
- 不用考虑内存不够的问题,但是读取速度就变慢了,但是如果建立起缓存的话,还是会变快。
- 状态后端的选择可以在Flink的配置文件中进行修改,
- RocksDB支持增量化的状态更新。
- 选择filesystem的话,要设置HDFS的存储路径。
- 设置状态的区域性恢复策略
以上这些都可以在配置文件中进行设置,但同时也可以在代码中进行设置。
// 状态后端配置
env.setStateBackend( new MemoryStateBackend());
env.setStateBackend( new FsStateBackend("url"));
env.setStateBackend( new RocksDBStateBackend("url"));
需要在env或者StreamExecutionEnvironment中的静态方法。
/** The default maximal size that the snapshotted memory state may have (5 MiBytes). */
public static final int DEFAULT_MAX_STATE_SIZE = 5 * 1024 * 1024;
/** The maximal size that the snapshotted memory state may have. */
private final int maxStateSize;
/** Switch to chose between synchronous and asynchronous snapshots.
* A value of 'UNDEFINED' means not yet configured, in which case the default will be used. */
private final TernaryBoolean asynchronousSnapshots;
public MemoryStateBackend(boolean asynchronousSnapshots)
public MemoryStateBackend(int maxStateSize)
public MemoryStateBackend(int maxStateSize, boolean asynchronousSnapshots)
内存状态后端可以设置保存内存状态快照的size——
maxStateSize
,最大内存5M,设置异步快照asynchronousSnapshots
。
public FsStateBackend(String checkpointDataUri)
public FsStateBackend(String checkpointDataUri, boolean asynchronousSnapshots)
public FsStateBackend(Path checkpointDataUri)
public FsStateBackend(Path checkpointDataUri, boolean asynchronousSnapshots)
文件系统状态后端,需要设置checkpoint在HDFS上的保存路径
checkpointDataUri
,以及异步快照asynchronousSnapshots
。
public RocksDBStateBackend(String checkpointDataUri)
public RocksDBStateBackend(String checkpointDataUri, boolean enableIncrementalCheckpointing)
public RocksDBStateBackend(URI checkpointDataUri)
public RocksDBStateBackend(URI checkpointDataUri, boolean enableIncrementalCheckpointing)
RocksDB状态后端,需要设置checkpoint在RocksDB上的保存路径
checkpointDataUri
,以及增量检查点更新enableIncrementalCheckpointing
,固定使用异步快照。
参考资料
《Flink任务总是down?你选对了State Backends吗?》