State概念

state 就是流式计算中持久化了的状态。与Spark Stream中的State类似,但是功能更加强大,能存储的内容更多。Spark Stream的State是一种轻量级的。而Flink中的State则是可以和checkpoint和statebackend配合,可以存储很大一部分数据。

State两种形式

state有两种形式,分别为:OperatorState 以及 KeyedState。主要是看前面有没有keyby操作 

OperatorState没有current key概念。KeyedState的数值总是与一个current key对应。

OperatorState只有堆内存一种实现。KeyedState有堆内存和RocksDB两种实现。

OperatorState需要手动实现snapshot和restore方法。KeyedState由backend实现,对用户透明。

State-Backend分类

有三种State-Backend:MemoryStateBackend、FsStateBackend、RocksDBStateBackend。

对于OperatorState来说,三种backend都使用DefaultOperatorStateBackend。将数据存储到内存中。

对于KeyedState来说,MemoryStateBackend和FsStateBackend都会使用HeapKeyedStateBackend,两者的区别点是,MemoryStateBackend会将数据直接传给master节点,数据不落盘,而HeapKeyedStateBackend,如果当数据到达一定量的时候,就会将数据写入文件系统中,然后将路径告诉master节点。RocksDBStateBackend使用的是RocksDBKeyedStateBackend,它会将数据写入RocksDB中,然后将路径告诉master节点。

StateBackend选择

一般来说是在FsStateBackend和RocksDBStateBackend之间选择

FsStateBackend:性能更好,日常存储是在堆内存中,面临着OOM的风险,不支持增量checkpoint

RocksDBStateBackend:将数据序列化成byte数组存储,读的时候还需要反序列,无需担心OOM风险,一般来说,多数是选这种Backend多一点。

RocksDB的state存储

spark timstamp 类型 spark state_默认值

RocksDB中,每个state使用一个Column Family

每个column family使用独占write buffer,整个DB共享一个block cache。

数据是先写入write buffer中,达到一定阈值后,在写入rocksdb中。

 

 

state.backend.rocksdb.block.blocksize : 数据块大小,默认4KB 增大会影响减少内存使用,但是会影响读性能。

state.backend.rocksdb.block.cache-size:整个DB的block cache大小,默认8MB,建议调大

state.backend.rocksdb.compaction.level.use-dynamic-size:如果使用LEVEL compaction,在SATA磁盘上,建议配成true,默认false

state.backend.rocksdb.files.open : 最大打开文件数目,-1 意味着没有限制,默认值 5000

state.backend.rocksdb.thread.num:后台flush和compaction的线程数。默认值为1,建议调大。

state.backend.rocksdb.writebuffer.count:每个column family 的writebuffer数目,默认值2,建议调大

state.backend.rocksdb.writebuffer.number-to-merge:写之前的write buffer merge数目,默认值1.建议调大

state.backend.rocksdb.writebuffer.size:每个write buffer的size,默认值4MB,建议调大

 

State使用心得

operatorState使用建议:

慎重使用长list

spark timstamp 类型 spark state_数据_02

在flink中,使用的是long的数组去接受 listValue中offset的位置,如果listValue长度很长,那么这个数组就会很大,就会造成很大的内存使用,并且,这部分内存是传给jobmanager的。

 KeyedState使用建议:

如何清空当前state?

state.clear() 只能清理当前key对应的value值

需要借助KeyedStateBackend的 applyToAllKeys方法

当value值很大的极限场景?

受限于JNI bridge API的限制,单个value只支持2^31 bytes(2G)

考虑使用MapState来替代ListState或者ValueState。因为ListState和ValueState是整个都是序列化存储在一个key-value中。而mapstate可以根据key,来存在多个key-value中。

RocksDB的日志可以观察到一些compaction信息,默认存储在flink-io目录下,需要登入到taskmanager里面才能找到。

TTL清理策略

flink state是可以做TTL的,这里不做细讲,这里只说清理策略。

默认情况下,配置了TTL,那么只有在下次读访问的时候,才会触发清理那条过期数据,如果那条数据之后不再被访问,那么该条过期数据也不会被清理。

三种策略:

1.  StateTtlConfig.newBuilder(Time.days(7)).cleanupFullSnapshot().bulid();   Full snapshot时候会清理snapshot内容,但是不会清理本地的缓存。

2.  StateTtlConfig.newBuilder(Time.days(7)).cleanupIncrementally(10,false).bulid();   Heap state backend的持续清理,这里表示每访问10次状态的数据,就去清理过期数据。如果第二个参数为true,就是,每访问10次record(流中的数据),就去清理过期数据。

3.StateTtlConfig.newBuilder(Time.days(7)).cleanupInRocksdbCompactFilter().build();  RocksDB state backend的持续清理,每次compact的时候清理。

注意:如果使用了Flink SQL,那么清理state会由flink自动接管,当然,如果自定义了UDF,并且在UDF中使用了状态,那么这部分的state,还是要由我们设置TTL来清理。

RocksDBState使用建议

不要创建过多的state,因为:

每个state一个column family,独占write buffer,过多的state会导致占据过多的write buffer。

根本上还是RocksDB StateBackend的native内存无法直接管理

Checkpoint使用建议

一般5min级别足够

checkpoint与record处理共抢一把锁,Checkpoint的同步阶段会影响record的处理

合理设置超时时间

默认的超时时间是10min,如果state规模大,则需要合理配置。最坏情况是创建速度大于删除速度,导致磁盘空间不可用。