Flink的可靠性保证 - 状态存储

一 为什么需要State存储

与批计算相比,State是流计算特有的,批计算的failover机制,是失败后重新计算;流计算在大多数场景下是增量计算,数据逐条处理,每次计算是在上一次计算结果之上进行处理的,这就要求对上一次的计算结果进行存储,当因为机器,网络,脏数据等原因导致程序错误的时候,可以重启Job进行state恢复。Flink就是基于state存储,通过CheckPoint机制来保证数据的准确性。

此外,State存储的内容还有流计算过程中计算节点的中间结果或元数据属性,比如Window方面的操作,需要累加数据;在aggregation过程中的中间聚合结果;在以Apache Kafka作为数据源时候,记录已经读取数据的offset等

二 State存储的实现

Flink内部有三种state的存储实现,如果不做配置,Flink默认使用的是MemoryStateBackend,三种实现分别是:

1 基于内存的MemoryStateBackend- 在debug模式使用,在生产模式下不建议使用;

2基于HDFS的FsStateBackend –基于分布式文件系统的持久化,每次读写都产生网络IO,整体性能不太好;

3基于RocksDB的RocksDBStateBackend - 本地文件+异步HDFS持久化,当前版本在生产环境下使用的

选择用RocksDB+HDFS的方式进行State的存储,State存储分两个阶段,首先本地存储到RocksDB,然后异步的同步到远程的HDFS。 这样的而设计既消除了HeapStateBackend的局限(内存有限,宕机数据丢失),也减少了纯分布式存储的网络IO开销。RocksDBStateBackend存储value的大小是有限制的,

RocksDB’s的bridge API是基于byte[]的,所以这种state存储支持的每个key的value最大不超过2^31,有些merge操作的值可能会超过2^31 bytes,这点要注意。

StateBackend的控制粒度到job级别,如果想为所有job设置StateBackend,可以通过更改flink-conf.yaml文件里state.backend的值 ,上述3类sate backend对应的值是;

jobmanager(MemoryStateBackend),filesystem(FsStateBackend),和 rocksdb(RocksDBStateBackend);也可为单独的job设置StateBackend的方法:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setStateBackend(new FsStateBackend("hdfs://namenode:40010/flink/checkpoints"));

KeyedState和OperatorState:

KeyedState - 这里面的key就是我们用KeyBy(x)里面的key,key与key之间的State是不可见的。KeyedState只能用在KeyedStream上的数据处理上。OperatorState是和Operator联系在一起的,比如Source Connector的实现中会用OperatorState来记录source数据读取的offset。

无论选用何种stateBackend,这些state都是优先存在本机上,当计算并行度发生变化,这些state也会被重新分发到不同机器上去。

OperatorState的分发,需要在Source Connector中实现,重点是把source的partition重新分配,并把之前记录的每个partition的offset也告诉新分配到的Source Connector。

KeyedState存的数据量比较大,如果调整并发度,copy的东西可能比较多,Flink为了避免过多的拷贝,采用了一个keygroup的机制。每个key通过hash方法分配到不同的keygroup中,当并发度调整的时候,调整粒度是keygroup,也就是一个key通过hash后所在的keygroup保持不变。

RawState 和  Managed State

managed state是我们常用的那些ValueState,ListState,MapState等,这些State类型,由Flink控制它们的数据结构和存取方法。

Raw state是在自己实现operator的时候使用,相当于自定义state类型,自己控制数据结构和存取方式。

State可以手动地删除已存的值,也可以设置Time-To-Live (TTL),让state过期自动失效。存取State前,要先创建StateDescriptor,StateDescriptor含有state名称和state值的数据类型,有时候还需要自定义函数。在keyedstream上应用的函数,存取state的时候,key是由Flink自动提供的,直接使用xxx.value()函数就可以取到当前key对应的值;由Flink自动控制的话,可以统一控制state和stream的分区。

欢迎阅读,有问题可以通过邮件kaiyuan0989爱特163.com一起探讨。

你可能感兴趣的:(Flink的可靠性保证 - 状态存储)