一个时间的生存期(TTL)可以被分配给任何类型的键控状态。如果已配置TTL并且状态值已过期,则将尽力清除存储的值,下面将对此进行详细讨论。所有状态收集类型均支持按条目TTL。这意味着列表元素和映射条目独立过期。为了使用状态TTL,必须首先构建一个StateTtlConfig配置对象。然后可以通过传递配置在任何状态描述符中启用TTL功能:
//创建对应状态描述符
val vsd = new ValueStateDescriptor[Int]("wordCount",createTypeInformation[Int])
//设置TTL的实效性
val stateTtlConfig = StateTtlConfig.newBuilder(Time.seconds(5)) //设置存活时间五秒
.setUpdateType(UpdateType.OnCreateAndWrite) //创建,修改重新更新时间
.setStateVisibility(StateVisibility.NeverReturnExpired) //永远不返回过期数据
.build()
vsd.enableTimeToLive(stateTtlConfig)
//获取RuntimeContext
val context:RuntimeContext = getRuntimeContext
//获取指定的类型状态
vs = context.getState(vsd)
①:该参数指定State存活时间,必须指定。
②:该参数指定State实效时间更更新时机,默认值OnCreateAndWrite
OnCreateAndWrite : 只有修改操作,才会更更新时间
OnReadAndWrite:只有访问读取、修改state时间就会更更新
③:设置state的可见性,默认值NeverReturnExpired
NeverReturnExpired:永不不返回过期状态ReturnExpiredIfNotCleanedUp:如果flink没有删除过期的状态数据,系统会将过期的数据返回
注意:一旦用户开启了TTL特征,系统每个存储的状态数据会额外开辟8bytes(Long类型)的字节大小,用于存储state时间戳;系统的时效时间目前仅支持的是计算节点时间;如果程序一开始没有开启
TTL,在服务重启以后,开启了TTL,此时服务在故障恢复的时候,会报错!
在flink-1.10版本中,系统可以根据State backend配置,定期在后台收集失效状态进行删除。⽤户可以通过调用以下API关闭自动清理。
val stateTtlConfig = StateTtlConfig.newBuilder(Time.seconds(5)) //设置存活时间5s
.setUpdateType(UpdateType.OnCreateAndWrite) //创建、修改重新更更新时间
.setStateVisibility(StateVisibility.NeverReturnExpired) //永不不返回过期数据
.disableCleanupInBackground()//关闭自动清理
.build()
早期版本需要用户手动调用cleanupInBackground开启后台清理。flink-1.10版本该特性自动打开
此外,您可以在拍摄完整状态快照时激活清除操作,这将减小其大小。在当前实现下不会清除本地状态,但是如果从先前的快照还原,则不会包括已删除的过期状态。可以在StateTtlConfig以下位置进行配置:
可以通过配置Cleanup in full snapshot机制,在系统恢复的时候或者启动的时候, 系统会加载状态数
据,此时会将过期的数据删除。也就意味着系统只用在重启或者恢复的时候才会加载状态快照信息。
val stateTtlConfig = StateTtlConfig.newBuilder(Time.seconds(5)) //设置存活时间5s
.setUpdateType(UpdateType.OnCreateAndWrite) //创建、修改重新更更新时间
.setStateVisibility(StateVisibility.NeverReturnExpired) //永不不返回过期数据
.cleanupFullSnapshot()//关机重启底层做快照,同时删除过期的数据
.build()
缺点:需要定期的关闭服务,进行服务重启,实现内存释放。
增量清理
另一个选择是逐步触发某些状态条目的清除。触发器可以是来自每个状态访问或/和每个记录处理的回调。如果此清理策略在特定状态下处于活动状态,则存储后端将在其所有条目上为此状态保留一个惰性全局迭代器。每次触发增量清理时,迭代器都会前进。检查遍历的状态条目,并清理过期的条目。
//设置TTL实效性
val stateTtlConfig = StateTtlConfig.newBuilder(Time.seconds(5)) //设置存活时间5s
.setUpdateType(UpdateType.OnCreateAndWrite) //创建、修改重新更更新时间
.setStateVisibility(StateVisibility.NeverReturnExpired) //永不不返回过期数据
.cleanupIncrementally(100,true)
.build()
cleanupSize: 表示一次检查key的数目
runCleanupForEveryRecord:是否在有数据时候就触发检查,如果为false,表示只有在状态访问或
者修改的时候才会触发检查
此策略有两个参数。第一个是每个清除触发的已检查状态条目数。它总是在每个状态访问时触发。第二个参数定义是否在每个记录处理中额外触发清理。堆后端的默认后台清理会检查5个条目,而每个记录处理不会进行清理。
如果使用RocksDB状态后端,则将调用Flink特定的压缩过滤器进行后台清理。RocksDB定期运行异步压缩以合并状态更新并减少存储。Flink压缩过滤器使用TTL检查状态条目的过期时间戳,并排除过期值。
val stateTtlConfig = StateTtlConfig.newBuilder(Time.seconds(5)) //设置存活时间5s
.setUpdateType(UpdateType.OnCreateAndWrite) //创建、修改重新更更新时间
.setStateVisibility(StateVisibility.NeverReturnExpired) //永不不返回过期数据
.cleanupInRocksdbCompactFilter(1000)
.build()
queryTimeA"erNumEntries:RocksDB进行合并扫描多少条记录之后,执行一次查询,将过期数据删除。
更频繁地更新时间戳可以提高清除速度,但由于使用本地代码中的JNI调用,因此会降低压缩性能。每次处理1000个条目时,RocksDB后端的默认后台清理都会查询当前时间戳。
在flink-1.10版本之前,RocksDB的Compact Filter特性是关闭的,需要额外的开启,用户只需在flink-conf.yaml中添加如下配置
state.backend.rocksdb.ttl.compaction.filter.enabled: true
由于Flink是一个有状态计算的流服务,因此状态的管理理和容错是非常重要的。为了保证程序的健壮性,Flink提出Checkpoint机制,该机制用于持久化计算节点的状态数据,继而实现Flink故障恢复。所谓的Checkpoint机制指的是Flink会定期的持久化的状态数据。将状态数据持久化到远程文件系统(取决于State backend),例如HDFS,该检查点协调或者发起是由JobManager负责实施。JobManager会定期向下游的计算节点发送Barrier(栅栏),下游计算节点收到该Barrier信号之后,会预先提交自己的状态信息,并且给JobManage以应答,同时会继续将接收到的Barrier继续传递给下游的任务节点,一次内推,所有的下游计算节点在收到该Barrier信号的时候都会做预提交自己的状态信息。等到所有的下游节点都完成了了状态的预提交,并且JobManager收集完成所有下游节点的应答之后,JobManager才会认定此次的Checkpoint是成功的,并且会自动删除上一次检查点数据。
Savepoint是手动触发的Checkpoint,Savepoint为程序创建快照并将其写到State Backend。Savepoint依靠常规的Checkpoint机制。所谓的Checkpoint指的是程序在执行期间,程序会定期在工作节点上快照并产生Checkpoint。为了进行恢复,仅需要获取最后一次完成的Checkpoint即可,并且可以在新的Checkpoint完成后立即安全地丢弃较旧的Checkpoint。Savepoint与这些定期Checkpoint类似,Savepoint由用户触发并且更新的Checkpoint完成时不会自动过期。用户可以使用命令行或通过REST API取消作业时创建Savepoint由于Flink 中的Checkpoint机制默认是不不开启的,需要用户通过调用以下方法开启检查点机制。为了控制检查点执行的一些细节,Flink支持用户定制Checkpoiont的一些行为。
//间隔5s执行一次checkpoint 精准一次
env.enableCheckpointing(5000,CheckpointingMode.EXACTLY_ONCE)
//设置检查点超时 4s
env.getCheckpointConfig.setCheckpointTimeout(4000)
//开启本次检查点 与上一次完成的检查点时间间隔不得小于 2s 优先级高于 checkpoint interval
env.getCheckpointConfig.setMinPauseBetweenCheckpoints(2000)
//如果检查点失败,任务宣告退出 setFailOnCheckpointingErrors(true)
env.getCheckpointConfig.setTolerableCheckpointFailureNumber(0)
//设置如果任务取消,系统该如何处理理检查点数据
//RETAIN_ON_CANCELLATION:如果取消任务的时候,没有加--savepoint,系统会保留检查点数据
//DELETE_ON_CANCELLATION:取消任务,自动是删除检查点(不建议使用)
env.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.
RETAIN_ON_CANCELLATION)
Flink指定多种State Backend实现,State Backend指定了状态数据(检查点数据)存储的位置信息。配置Flink的状态后端的方式有两种:
每个计算独立状态后端
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setStateBackend(...)
全局默认状态后端,需要在flink-conf.yaml配置
#==============================================================================
# Fault tolerance and checkpointing
#==============================================================================
# The backend that will be used to store operator state checkpoints if
# checkpointing is enabled.
#
# Supported backends are 'jobmanager', 'filesystem', 'rocksdb', or the
# <class-name-of-factory>.
#
state.backend: rocksdb
# Directory for checkpoints filesystem, when using any of the default bundled
# state backends.
#
state.checkpoints.dir: hdfs:///flink-checkpoints
# Default target directory for savepoints, optional.
#
state.savepoints.dir: hdfs:///flink-savepoints
# Flag to enable/disable incremental checkpoints for backends that
# support incremental checkpoints (like the RocksDB state backend)
# state.backend.incremental: false
由于状态后端需要将数据同步到HDFS,因此Flink必须能够连接HDFS,所以需要在~/.bashrc配置 HADOOP_CLASSPATH
JAVA_HOME=/usr/java/latest
HADOOP_HOME=/usr/hadoop-2.9.2
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
CLASSPATH=.
export JAVA_HOME
export CLASSPATH
export PATH
export HADOOP_HOME
export HADOOP_CLASSPATH=`hadoop classpath`
MemoryStateBackend 使用内存存储内部状态数据,将状态数据存储在在Java的heap中。在Checkpoint时候,此状态后端将对该状态进行快照,并将其作为检查点确认消息的一部分发送给JobManager(主服务器),该JobManager也将其存储在其堆中。
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setStateBackend(new MemoryStateBackend(MAX_MEM_STATE_SIZE, true))
限制:
The size of each individual state is by default limited to 5 MB. This value can be increased in the constructor of the MemoryStateBackend. Irrespective of the configured maximal state size, the state cannot be larger than the akka frame size (see Configuration). The aggregate state must fit into the JobManager memory.
场景: 1)本地部署进行debug调试的可以 2)不仅涉及太多的状态管理理。
该种状态后端实现是将数据的状态存储在TaskManager(计算节点)的内存。在执行检查点的时候后会将TaskManager内存的数据写入远程的文件系统。非常少量的元数据想信息会存储在JobManager的内存中。
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setStateBackend(new FsStateBackend("hdfs:///flink-checkpoints",true))
场景:1)当用户有非常大的状态需要管理 2)所有生产环境
该种状态后端实现是将数据的状态存储在TaskManager(计算节点)的本地的RocksDB数据文件中。在执行检查点的时候后会将TaskManager本地的RocksDB数据库文件写入远程的文件系统。非常少量量的元数据想信息会存储在JobManager的内存中。
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-statebackend-rocksdb_2.11</artifactId>
<version>1.10.0</version>
</dependency>
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStateBackend(new RocksDBStateBackend("hdfs:///flink-rocksdb-checkpoints",true))
由于RocksDB的JNI桥API基于byte[],所以每个键和每个值支持的最大大小为每个231字节。重要提示:在RocksDB中使用合并操作的状态(例如ListState)可以静默地累积值大小>231字节,然后在下次检索时失败。这是目前的限制罗克斯德布JNI。
场景:1)当用户有超大的状态需要管理 2)所有生产环境