生产环境中,Flink Job运行过程中会定期执行CheckPoint,将内部状态持久化到外部的存储系统中,一般会选择使用HDFS。多种外部存储系统之间的差异详见之前写的那篇文章《Flink的状态后端(State Backends)》。
一般建议选用RocksDB作为StateBackend。RocksDB使用的LSM树,会将状态存储到磁盘sst文件(类似HBase中的HFile)中,避免State过大将内存撑爆的问题,并且RocksDB支持增量存储和全量存储(默认采用的是全量)。增量和全量指的是每次从本地上传sst文件到hdfs是增量还是全量。
但是随着程序不停的执行, CheckPoint文件会越来越多,那么该如何清理过期的文件呢?
每个文件夹都对应一个Job的完整的Checkpoint目录,如果同一个Jar包多次提交,那么就会生成多个不同的目录:
每个目录中结构如下:
一个chk-X代表了一次Checkpoint信息,里面存储Checkpoint的元数据和数据。
taskowned: TaskManagers拥有的状态
shared: 共享的状态
任务Cancel时,Flink支持两种清除策略:
/** * Delete externalized checkpoints on job cancellation. * *
All checkpoint state will be deleted when you cancel the owning * job, both the meta data and actual program state. Therefore, you * cannot resume from externalized checkpoints after the job has been * cancelled. * *
Note that checkpoint state is always kept if the job terminates * with state {@link JobStatus#FAILED}. */ DELETE_ON_CANCELLATION(true), /** * Retain externalized checkpoints on job cancellation. * *
All checkpoint state is kept when you cancel the owning job. You * have to manually delete both the checkpoint meta data and actual * program state after cancelling the job. * *
Note that checkpoint state is always kept if the job terminates * with state {@link JobStatus#FAILED}. */ RETAIN_ON_CANCELLATION(false);
默认情况下,如果设置了Checkpoint选项,Flink只保留最近成功生成的1个Checkpoint。当Flink程序失败时,可以从最近的这个Checkpoint来进行恢复。但是,如果我们希望保留多个Checkpoint,并能够根据实际需要选择其中一个进行恢复,这样会更加灵活。Flink支持保留多个Checkpoint,需要在Flink的配置文件conf/flink-conf.yaml中,添加如下配置指定最多需要保存Checkpoint的个数,例如指定保留最近的10个Checkpoint(就是保留上面的10个chk-X):
state.checkpoints.num-retained: 10
ps1:Checkpoint目录如果删除,任务就无法指定从Checkpoint恢复了
ps2:如果job是失败了而不是手动cancel,那么无论选择上面哪种策略,state记录都会保留下来
ps3:使用RocksDB来作为增量checkpoint的存储,可以进行定期Lazy合并清除历史状态。
Checkpoint有全量和增量两种,如果是全量的话就比较简单了,直接删除Checkpoint目录即可。但是如果增量的话就不能随意删除目录了,因为最新的CheckPoint目录中的数据可能依赖于前几次的CheckPoint中的信息。如下图所示:
RocksDB存储是基于LSM Tree 实现的,会定期对磁盘上多个 sstable 文件进行合并操作,合并时会将删除的、过期的以及旧版本的数据进行清理,从而降低 sstable 文件的总大小。图中可以看到第三次 Checkpoint 时生成的快照信息为sstable3、sstable4、sstable5 及元数据文件 MANIFEST-chk3, 其中新增了 sstable4 文件且 sstable1 和 sstable2 文件合并成 sstable5 文件,因此第三次 Checkpoint 时只需要向外部持久化存储上传 sstable4、sstable5 及元数据文件 MANIFEST-chk3。
假设job X 运行过程中,仅仅做了一次 Checkpoint,也就是图中的 Checkpoint 1,之后由于某些原因任务挂了,所以任务从 Checkpoint 1 处恢复任务,恢复后的任务为 job Y。job Y 恢复后运行一段时间后进行第二次 Checkpoint,也就是图中的 Checkpoint 2,由于使用的基于 RocksDB 的增量 Checkpoint,因此 Checkpoint 2 只需要将 MANIFEST-chk2 和 sstable3 上传到 hdfs 即可,此时 job Y 的 checkpoint 目录仅仅包含 sstable3,而 sstable1、sstable2 依然保存在 job X 的 Checkpoint 目录中。如果此时贸然吧job X对应的目录删了,那么指定从Checkpoint2中恢复就会失败!
该图详细的解释详见参考中的“一”,博主已经讲的非常详细了,这里就不说了。
个人建议还是不要去手动删除Checkpoint目录的好,以免出现问题。Flink1.6之后支持对State设置TTL。如果你的流式应用的State很大并且会逐渐增大时,强烈建议对State设置TTL,清除掉State中无用的信息。示例如下:
/**
* 必须在Flink配置启用TTL功能:state.backend.rocksdb.ttl.compaction.filter.enabled 设置为true
*/
StateTtlConfig config = StateTtlConfig // 生命周期为10分钟 .newBuilder(Time.minutes(10)) // 目前仅支持ProcessingTime .setTtlTimeCharacteristic(StateTtlConfig.TtlTimeCharacteristic.ProcessingTime) // 过期值永不返回 还有一种是如果没有删除,值仍然返回 .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) // 设置State更新类型: 创建和写入权限(默认) 还有两种是:状态不过期 读和写 .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) // RocksDB压缩过滤清除(后台清除),还有其他几种策略 // 1000:表示在更新当前时间戳之前由压缩筛选器处理的状态条目数。默认就是1000L .cleanupInRocksdbCompactFilter(1000L) .build();
/**
* 然后将TTL信息设置到State中
*/
XXXStateDescriptor.enableTimeToLive(ttlConfig);
Checkpoint | Savepoint |
---|---|
由 Flink 的 JobManager 定时自动触发并管理 | 有用户手动触发并管理 |
主要用于任务发生故障时,为任务提供自动恢复机制 | 主要时用户升级Flink版本、修改任务的代码逻辑、调整算子并行度等,且必须手动恢复 |
当时用RocksDBStateBackend时,支持增量房是对状态信息进行快照 | 仅支持全量快照 |
Flink任务停止后,Checkpoint的状态快照信息默认被清除 | 一旦触发 Savepoint,状态信息就被持久化到外部存储,除非用户手动删除 |
Checkpoint 设计目标:轻量级且尽可能快地恢复任务 | Savepoint 的生成和恢复成本会更高一些,Savepoint 更多地关注代码的可移植性和兼容任务的更改操作 |
如果任务只是由于各种原因需要重启,不涉及到代码逻辑,算子并行度的调整,那么可以直接手动从Checkpoint恢复,例如:
bin/flink run -s hdfs://namenode/flink/flink-checkpoints/582e17d2cc343e6c56255d111bae0191/chk-860/_metadata app.jar
如果将-s指定为savepoint的路径,那么就会从savepoint中恢复。
如果是任务自身重启的话,默认会自动从CheckPoint恢复。如果是手动cancel掉的,那么需要手动-s 指定从哪里开始恢复。
参考:
https://www.jianshu.com/p/165a1bf33e4a?utm_campaign=haruki(Flink清理过期CheckPoint目录的正确姿势)
https://blog.csdn.net/hxcaifly/article/details/84673292(Flink 设置保存多份CheckPoint)
https://blog.csdn.net/u013411339/article/details/90625604(Flink1.8之后支持自动删除过期状态)
https://blog.csdn.net/hxcaifly/article/details/84673292(Flink Job任务恢复)