Flink源码解析(二十一) ——Checkpoint过程解析(下)

一、状态存储及快照策略说明

1、Flink用户视角下状态存储新旧版本区分

(1)、旧版本StateBackend
旧版本StateBackend分类:MemoryStateBackend、FsStateBackend、RocksDBStateBackend
旧版本StateBackend用法:
1)、单任务配置
  env.setStateBackend(new FsStateBackend("hdfs://hdfsha/flink/checkpoints"));
  env.setStateBackend(new MemoryStateBackend());
  env.setStateBackend(new RocksDBStateBackend(filebackend, true));
2)、全局配置flink-conf.yaml
  state.backend: filesystem
  state.checkpoints.dir: hdfs://hdfsha/flink/checkpoints

注:旧版本StateBackend机制模糊了作业运行过程中状态存储、访问过程和作业checkpoint过程中状态持久化过程,新版本用CheckpointStorage强调了下Checkpoint过程,将这俩过程做了区分。

(2)、新版本StateBackend
新版本StateBackend分类:HashMapStateBackend、EmbeddedRocksDBStateBackend
新版本StateBackend用法:
1)、HashMapStateBackend 替代 旧版本MemoryStateBackend
  env.setStateBackend(new HashMapStateBackend());  //更偏向于作业运行过程中状态存储及访问过程
  env.getCheckpointConfig().setCheckpointStorage(new JobManagerCheckpointStorage());  //更偏向于作业checkpoint过程中状态持久化过程
2)、HashMapStateBackend 替代 旧版本FsStateBackend
  env.setStateBackend(new HashMapStateBackend());  //更偏向于作业运行过程中状态存储及访问过程
  env.getCheckpointConfig().setCheckpointStorage("hdfs://checkpoints");  //更偏向于作业checkpoint过程中状态持久化过程
3)、EmbeddedRocksDBStateBackend 替代 旧版本RocksDBStateBackend
  env.setStateBackend(new EmbeddedRocksDBStateBackend());  //更偏向于作业运行过程中状态存储及访问过程
  env.getCheckpointConfig().setCheckpointStorage("hdfs://checkpoints");  //更偏向于作业checkpoint过程中状态持久化过程

2、Flink系统视角下状态存储

1)、Flink系统视角下更关注状态分类即OperatorState和KeyedState,在Checkpoint状态持久化过程中用DefaultOperatorStateBackend实现OperatorState持久化,用HeapKeyedStateBackend实现KeyedState持久化。Flink用户视角下状态存储StateBackend会新建DefaultOperatorStateBackend和HeapKeyedStateBackend。

2)、快照策略SnapshotStrategy,快照策略代表同一个状态存储不同的快照方法过程。

DefaultOperatorStateBackend对应的快照策略是DefaultOperatorStateBackendSnapshotStrategy

HeapKeyedStateBackend对应的快照策略是HeapSnapshotStrategy

RocksDBKeyedStateBackend对应的快照策略有2个,全量快照策略RocksNativeFullSnapshotStrategy和增量快照策略RocksIncrementalSnapshotStrategy

上篇随笔中提到OperatorSnapshotFutures包含4个异步状态数据持久化动作,新建OperatorSnapshotFinalizer对象时构造函数中会异步执行这4个不同类型的状态快照过程,但没有解析状态数据Checkpoint过程细节。本篇随笔把托管的KeyedState、OperatorState快照过程详细解析下,即着重分析HeapKeyedStateBackend和DefaultOperatorStateBackend。

二、KeyedState快照过程

Flink系统通过方法HeapKeyedStateBackend.snapshop(...)触发KeyedState快照过程核心逻辑,最终方法链调用会转入到方法SnapshotStrategyRunner.snapshot(...)中去。

方法SnapshotStrategyRunner.snapshot(...)包含两个重要步骤,分别如下:

1、第77行:SR snapshotResources = snapshotStrategy.syncPrepareResources(checkpointId);同步拷贝状态数据的引用,而不是状态实例对象数据的拷贝。

2、第80行:snapshotStrategy.asyncSnapshot(...);方法里创建Checkpoint输出流CheckpointStateOutputStream、Checkpoint数据持久化操作。

下面详细解析这两个重要步骤:

1、snapshotStrategy.syncPrepareResources(checkpointId)跳转到方法HeapSnapshotResources.create(...)里拷贝状态引用。

方法HeapSnapshotResources.create(...)第一个参数registeredKVStates代表Flink应用业务代码里注册的状态描述符数据表,是一个Map结构类型。key是状态描述符的名称,比方说下面一行代码注册了一个key是"pv"的ValueState类型的状态数据表,value就是状态数据表,一般来说是CopyOnWriteStateTable类型。假如一个KeyedStream流用userId字段做keyby分组,registeredKVStates的value就是同一个算子上所有userId的pv状态的数据值。

ValueStateDescriptor<Long> descriptor = new ValueStateDescriptor<>("pv",Long.class);

以下方法processSnapshotMetaInfoForAllStates(...)作用是拷贝状态数据的引用,而不是状态实例对象数据的拷贝。该过程执行比较快,可以同步执行。针对Flink业务代码里每一个状态描述符数据表,创建一个CopyOnWriteStateTableSnapshot快照放到cowStateStableSnapshots里。CopyOnWriteStateTableSnapshot快照的主要行为是将CopyOnWriteStateTable中的状态数据拷贝到CopyOnWriteStateTableSnapshot.stateMapSnapshots数组里。类CopyOnWriteStateTable的成员变量keyGroupedStateMaps实际类型是CopyOnWriteStateMap,CopyOnWriteStateMap牺牲部分性能和内存效率换来增量hash、写时复制的异步快照功能。

2、创建Checkpoint输出流CheckpointStateOutputStream、Checkpoint的持久化操作。

方法HeapSnapshotStrategy.asyncSnapshot(...)实现逻辑里主要包括CheckpointStateOutputStream输出流创建、Checkpoint的持久化操作等行为。

下面详细分析方法HeapSnapshotStrategy.asyncSnapshot(...)的功能。

(1)、创建Checkpoint输出流CheckpointStateOutputStream,在生产环境中一般用文件系统FsStateBackend做Checkpoint状态持久化操作,比如HDFS系统,对应的输出流类型是FsCheckpointStateOutputStream。

FsCheckpointStateOutputStream主要成员信息如下:

writeBuffer:状态数据写入缓冲数组byte[],数据先写到内存的缓存数组中,最后落盘,减少和文件系统的io交互。

pos:writeBuffer中数据写入的当前位置。

outStream:FSDataOutputStream类型,代表Checkpoint状态持久化文件路径名,通过该流成员将状态数据写到最终文件里。

basePath:checkpoint基础路径,来源于配置项:state.backend.fs.checkpointdir

fs:HDFS文件系统

localStateThreshold:状态数据大小阈值,默认值是20KB,上限是1MB。小于该阈值的状态数据会写到ByteStreamStateHandle字节数组成员中,以减少小文件数量。大于该阈值的状态数据落入文件中。

(2)、类文件HeapSnapshotStrategy第152行serializationProxy.write(outView); 利用CheckpointStateOutputStream,将状态数据的压缩格式、序列化类型、状态个数、状态名字等数据写入流中。  

(3)、 分组状态数据写入

简单来说,keyBy(...)操作按key将数据分组后以近乎随机的形式分配不同的key到不同的Task节点上。实际上Flink系统将不同的key分配到不同的keyGroup上,然后将一段连续的keyGroup分配到一个特定的Task节点上,这样实现不同的key到不同的Task节点。keyGroup的数量由StreamExecutionEnvironment.maxParallelism指定。

下图keyGroupRange指的就是一段连续的keyGroup,keyGroupRangeOffsets数组记录的是每个状态数据分组在状态文件里的位置。在将keyGroupId和状态id写入文件中后,接着写入分组后的状态数据。

分组状态数据的写入。

(4)、Checkpoint元数据返回

Flink系统将所有分组状态数据写完后紧接着收集Checkpoint元数据信息并返回。收集的信息主要有:

1)、keyGroupRangeOffsets,包含每个keyGoup的状态数据在状态文件中的存储位置。

2)、文件大小及文件名称等信息。该元数据信息通过SnapshotResult<StreamStateHandle> result = streamWithResultProvider.closeAndFinalizeCheckpointStreamResult();调用返回。

三、OperatorState快照过程

OperatorState快照过程和KeyedState快照过程基本类似,但比KeyedState快照过程简单。后续会完善OperatorState快照实现过程。

遗留问题:Flink1.18.0版本,KeyedState快照过程中一个Task上多个key数据对应一个连续keyGroup的keyGroupRange,key是怎么对应到分组范围内具体的keyGroup的。

posted @ 2024-03-03 14:27  有一个娃  阅读(201)  评论(0编辑  收藏  举报