flink的状态后端介绍

状态后端

使用DataStream API实现的程序一般都在下面的场景管理使用状态:

  • 窗口时间触发时刻的元素聚合操作
  • 转化方法中可能使用``key/value`状态接口去存储对应的值
  • 转化方法中可能实现``CheckpointedFunction`接口去实现自己的容错逻辑

当checkpoint启动的时候(下文简称checkpoint为cp,因为原名太长了),state会持久化保存到cp中用来防止数据丢失和一致性容错恢复。至于状态在内部存储形式和是何时何地存储状态的,要取决于使用的状态后端(State Backend)。

可用的状态后端

当前flink绑定了下面两种状态后端:

  • HashMapStateBackend
  • EmbeddedRocksDBStateBackend

如果没有显式指定,那么会flink会默认使用第一种HashMapStateBackend.

HashMapStateBackend

存储管理状态类似于管理一个java堆中的对象,key/value的状态和窗口操作都会在一个hash table中进行存储状态。

该后端建议用在如下场景:

  • 需要维护较大状态、长窗口、占用空间较大的key/value状态的job
  • 所有的高可用场景

同样也建议将managed memory参数设置为0。这样的话可以确保在JVM中将最大的内存分配给用户代码执行空间。

EmbeddedRocksDBStateBackend

该状态后端存储管理状态在RocksDB数据库中,该状态存储在本地taskManager的磁盘目录。与hash后端存储在堆生成对象不同,这种状态后端按照指定的类型序列化后变成字节数组将数据存储在磁盘,按照key的字典序排列而不是像hash那样使用hashcode()或者equals().

这种状态后端都是异步快照进行。

扫描二维码关注公众号,回复: 13504959 查看本文章

限制:

  • 因为RocksDB JNI的桥接API是基于byte[]的,所以每个key和每个value的最大占用空间不能超过2^31次方字节。但是在RocksDB中状态会使用合并操作(比如listState)。所以会出现多个key累加合并后大小超过限制空间,那么这个合并后的key在下次取用的时候就会报错,这是当前RocksDB JNI的限制。

该后端建议用下如下场景:(和上述一样,其实选择看个人,这两个状态后端都可以实现高可用)

  • 需要维护较大状态、长窗口、占用空间较大的key/value状态的job
  • 所有的高可用场景

要注意该后端你存储的state大小是依赖于你本地磁盘可用空间的,另外比起hash后端来说,这种方式利于保存较大的状态对象,因为磁盘总比内存大。不过虽然保存的状态大,但是因为数据存储到磁盘,但是流数据在内存中处理,那么需要把state序列化写磁盘和从磁盘反序列化读取状态,所以这部分的开销是比hash后端要高得多。所以这种后端方式在吞吐量方面会更低,因为读取和写入部分影响了处理性能。

这种状态后端是当前唯一的一种支持异步增量的cp。

选择正确的状态后端

当在hash后端和DB后端两者中不知道如何选择的时候,我们可以从性能和扩展性两方面来分析。

hash后端保存状态在内存中,所以拥有完美的性能,但是恰因为状态留在内存,所以在扩展性方面取决于当前集群的内存量。而另一方面,DB后端将状态存储在磁盘里,扩展性方面暂时不存在任何性能瓶颈,但是在保存状态和取用状态的时候需要通过序列化和反序列化,这样就导致了性能方便的确实,可能在处理过程中达不到hash后端性能的平均水平。

值得注意的是,不再需要为选择哪种后端而苦恼了。flink 1.13版本加入了一个新功能,可以保存二进制的保存点,然后程序从该点恢复,恢复的时候可以任意选择使用hash恢复还是DB恢复,这样一来就可以随意切换状态后端。如果你想切换,那么先手动生成一个保存点,然后恢复的时候选择你想要的后端方式即可。

但是前提是要升级到1.13版本。

配置状态后端(单个作业)

默认状态后端是jobmanager进行存储,也就是hash后端。同时如果你想为所有的job配置一些不同的后端,那么可以通过覆写flink-conf.yaml文件来修改。默认的状态后端可以按照单个job来修改。如下代码所示:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new HashMapStateBackend()); //默认的,不显式配置也是这个

如果想使用DB方式的状态后端,那么需要额外引入该方面的类包。

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-statebackend-rocksdb_2.11</artifactId>
    <version>1.13.2</version>
    <scope>provided</scope>
</dependency>

DB状态后端是作为flink分布式的一部分,如果不在代码中使用该状态管理那么不需要引入该依赖。也可以选择在flink-conf.yaml配置文件中通过指定state.backend来指定。

配置默认状态后端

默认的状态后端配置是在flink-conf.yaml下通过state.backend来指定的。(另外也可以使用全类名的方式来指定,比如:org.apache.flink.contrib.streaming.state.EmbeddedRocksDBStateBackendFactory

配置的值二选一:hashmap/rocksdb,分别代表了上文所描述的两种存储方式。

demo:

# The backend that will be used to store operator state checkpoints
state.backend: hashmap

# Directory for storing checkpoints
state.checkpoints.dir: hdfs://namenode:40010/flink/checkpoints

RocksDB状态后端详述

增量CP

上文也描述过了,DB式的状态后端是唯一可以进行增量快照的方式。这种方式在需要维护较大的状态的时候是十分有用的,因为每次的cp都会基于上一次完成cp进行,也就是说只记录修改,而不是进行一次全量的cp。

flink同时也会影响DB状态后端的内部合并操作,生成一种随着时间流逝,之前的多个cp增量会合并压实,也就是说多个cp不会无限增加。老的cp最后会被自动的包含和清理。

使用这种状态管理之后故障恢复的时间与全量cp相比速度不见得会快或者慢。如果网络带宽是当前系统瓶颈的话,那么可能会花费一点长的时间来从增量cp恢复,因为它会抓取更多的数据(更多的增量文件)。如果此时瓶颈在于CPU等那么因为在重新恢复的时候不需要重新构建新的cp,而是只要记录此cp和上个cp的变化即可。

设置增量cp:

  • 在配置文件·flink-conf.yaml·中设置·state.backend.incremental:true· 设置为true就会开启增量同步,除非在用户代码中修改该值,那么会按照代码中的设置

  • 代码中按照如下设置:

    EmbeddedRocksDBStateBackend backend = new EmbeddedRocksDBStateBackend(true);
    
    env.setStateBackend(backend); 
    

设置后需要注意,如果采取增量cp的方式,那么在flink-web ui中看到的state的大小是显示增量大小,而不是总的state的大小。

内存管理

flink指在可以控制管理所有的进程内存消耗确保flink的taskManager有着较好的运行方式,也就是意味着不会超过所给分配的最大内存限制,从而也不会被系统资源管理组件kill,不会出现资源浪费利用不足的情况。如非必要的溢写磁盘,缓存浪费、性能低下的情况。

为了实现这个,flink同时提供了默认的DB内存分配用来管理taskManager的内存(也可以理解为管理slot的资源)。这种方式可以开箱即用而不需要考虑内部复杂的实现原理,最主要的提高内存性能就是简单的提高flink的管理内存。

同时我们在使用的时候也可以不配置自动内存分配的设置而是针对每一个slot每一个state来进行内存划分,这提供了更细粒度的内存控制,但是需要注意的是需要注意内存的消耗不能超过资源环境的最高上限分配,尤其是在state比较大的场景。

RocksDB的内存管理

默认的内存管理配置如下:可通过该配置启停

·state.backend.rocksdb.memory.managed configuration key.·

flink不会直接管理RocksDB的本地内存,而是通过某种方式分配后恰好和之前定义的内存预算一样。这些都是在单独每一个slot上完成的分配方案。

为了设置RocksDB的全部实例对应的内存使用,flink在每一个单独的slot上增加了公布共享的cache和写缓存管理。共享的cache包括block cache,index和bloom filter 和MemTables。共享的cache上限是这四个组件一共的上限。

对于高级的设置,flink也支持控制写(MemTables)和读(index、filter,remianing cache)的内存切分使用,如果当你察觉到当前的性能执行低下是因为写缓存频繁刷新或者缓存丢失,可以使用如下参数进行重分配。

  • ·state.backend.rocksdb.memory.write-buffer-ratio· :默认是0.5,也就是默认分配的内存50%可用于读缓存。
  • ·state.backend.rocksdb.memory.high-prio-pool-ratio·:默认是0.1,意思是会分配共享块缓存中10%的内存作为index和filter的使用,这里尽量不要设置为0,去避免三者竞争而导致的性能问题。

时间定时器(Heap vs RocksDB)

定时器一般用户延迟操作调度,比如基于事件时间或者处理时间的窗口计算或者回调函数。

当使用了DB式的状态后端后,定时器自然也默认使用这种方式,当程序中出现了大量的定时器那么使用这种后端还可以保存大量的state,提高了程序扩展性。

另外,如果定时器不是很多,想要提高定时器的处理速度,那么将定时器保存在heap是一个更好的选择。flink支持定时器保存在堆中而其他状态使用DB状态后端管理维护。设置如下:

#默认是state.backend.rocksdb.timer-service.factory=rocksdb
state.backend.rocksdb.timer-service.factory: heap

注意:当状态后端使用DB且时间定时器保存在heap中,那么时间定时器的单独状态是无法通过增量CP的,而其他状态如keyedState等可以继续使用增量CP。

另外如果有高级开发在尝试写自定义流操作的时候,同时使用会导致cp和sp因为写原生keyed state失败。(不太理解,此处不深入研究)

旧状态后端移植(配置文件或者用户代码)

MemoryStateBackend

state.backend: hashmap

# Optional, Flink will automatically default to JobManagerCheckpointStorage
# when no checkpoint directory is specified.
state.checkpoint-storage: jobmanager
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new HashMapStateBackend());
env.getCheckpointConfig().setCheckpointStorage(new JobManagerStateBackend());

FsStateBackend

state.backend: hashmap
state.checkpoints.dir: file:///checkpoint-dir/

# Optional, Flink will automatically default to FileSystemCheckpointStorage
# when a checkpoint directory is specified.
state.checkpoint-storage: filesystem
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new HashMapStateBackend());
env.getCheckpointConfig().setCheckpointStorage("file:///checkpoint-dir");


// Advanced FsStateBackend configurations, such as write buffer size
// can be set by manually instantiating a FileSystemCheckpointStorage object.
env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage("file:///checkpoint-dir"));

RocksDBStateBackend

state.backend: rocksdb
state.checkpoints.dir: file:///checkpoint-dir/

# Optional, Flink will automatically default to FileSystemCheckpointStorage
# when a checkpoint directory is specified.
state.checkpoint-storage: filesystem
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new EmbeddedRocksDBStateBackend());
env.getCheckpointConfig().setCheckpointStorage("file:///checkpoint-dir");


// If you manually passed FsStateBackend into the RocksDBStateBackend constructor
// to specify advanced checkpointing configurations such as write buffer size,
// you can achieve the same results by using manually instantiating a FileSystemCheckpointStorage object.
env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage("file:///checkpoint-dir"));

猜你喜欢

转载自blog.csdn.net/weixin_41998764/article/details/120530988