Flink 清理过期 Checkpoint 目录的正确姿势

本文深入探讨Flink Checkpoint与Savepoint的区别,详解RocksDB增量Checkpoint原理,揭示生产环境中合理清理Checkpoint目录的重要性及正确策略。

 

 

本博客是笔者在生产环境使用 Flink 遇到的 Checkpoint 相关故障后,整理输出,价值较高的 实战采坑记,本文会带你更深入的了解 Flink 实现增量 Checkpoint 的细节。

通过本文,你能 get 到以下知识:

  • Flink Checkpoint 目录的清除策略
  • 生产环境应该选择哪种清除策略
  • 生产环境必须定期脚本清理 Checkpoint 和 Savepoint 目录
  • RocksDB 增量 Checkpoint 实现原理
  • 如何合理地删除 Checkpoint 目录?
  • 通过解析 Flink Checkpoint 的元数据信息来合理清理 Checkpoint 信息

1. 故障背景

本次故障涉及到的知识面比较多,将从以下多个角度来详细描述。

1.1 Flink Checkpoint 目录的清除策略

如下图所示,红圈处的一行配置 env.getCheckpointConfig().enableExternalizedCheckpoints() 表示当 Flink 任务取消时,是否保留外部保存的 CheckPoint 信息。

参数有两种枚举,分别是:ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION 和 ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION。这两种枚举分别代表什么含义呢?看一下源码中的解释:

  • DELETE_ON_CANCELLATION:仅当作业失败时,作业的 Checkpoint 才会被保留用于任务恢复。当作业取消时,Checkpoint 状态信息会被删除,因此取消任务后,不能从 Checkpoint 位置进行恢复任务。

  • RETAIN_ON_CANCELLATION:当作业手动取消时,将会保留作业的 Checkpoint 状态信息。注意,这种情况下,需要手动清除该作业保留的 Checkpoint 状态信息,否则这些状态信息将永远保留在外部的持久化存储中。

无论是选择上述哪种方式,后面都提示了一句:如果 Flink 任务失败了,Checkpoint 的状态信息将被保留。

1.2 生产环境中,该参数应该配置为 DELETE_ON_CANCELLATION 还是 RETAIN_ON_CANCELLATION ?

在 Flink 任务运行过程中,为了保障故障容错,会定期进行 Checkpoint 将状态信息保存到外部存储介质中,当 Flink 任务由于各种原因出现故障时,Flink 任务会自动从 Checkpoint 处恢复,这就是 Checkpoint 的作用。Flink 中还有一个 Savepoint 的概念,Savepoint 与 Checkpoint 类似,同样需要把状态信息存储到外部介质,当作业失败时,可以从外部存储中恢复。Savepoint 与 Checkpoint 的区别如下表所示:

Checkpoint Savepoint
由 Flink 的 JobManager 定时自动触发并管理 有用户手动触发并管理
主要用于任务发生故障时,为任务提供自动恢复机制 主要时用户升级Flink版本、修改任务的代码逻辑、调整算子并行度等,且必须手动恢复
当时用RocksDBStateBackend时,支持增量房是对状态信息进行快照 仅支持全量快照
Flink任务停止后,Checkpoint的状态快照信息默认被清除 一旦触发 Savepoint,状态信息就被持久化到外部存储,除非用户手动删除
Checkpoint 设计目标:轻量级且尽可能快地恢复任务 Savepoint 的生成和恢复成本会更高一些,Savepoint 更多地关注代码的可移植性和兼容任务的更改操作

相比 Savepoint 而言,Checkpoint 更加轻量级,但有些场景 Checkpoint 并不能完全满足我们的需求。所以在使用过程中,如果我们的需求能使用 Checkpoint 来解决优先使用 Checkpoint。当 Flink 任务中的一些依赖组件需要升级重启时,例如 hdfs、Kafka、yarn 升级或者 Flink 任务的 Sink 端对应的 MySQL

在 Apache Flink 中,**Checkpoint 是实现容错和持久化的核心机制**。它通过定期保存流式作业的全局状态(包括算子状态、键控状态等),使得在发生故障时能够从最近一次成功的 Checkpoint 恢复,从而保证 **Exactly-Once 语义**。 --- ## ✅ 什么是 CheckpointFlink 的 **Checkpoint** 是一个由 JobManager 协调发起的、分布式快照过程,用于记录当前所有任务的状态,并将其持久化到可靠的存储系统中。 > 💡 目标:当 Flink 作业因机器宕机、网络中断等原因失败后,可以从最近的 Checkpoint 恢复运行,不丢失数据也不重复处理。 --- ## 🧩 Checkpoint 的工作原理 1. JobManager 定期向所有 Source Task 发送 `Barrier`; 2. Barrier 随着数据流在 DAG 中传播; 3. 每个 Operator 接收到 Barrier 后暂停处理后续元素,将当前状态异步写入持久化存储; 4. 所有输入流都收到 Barrier 后完成本次 Checkpoint; 5. 最终 JobManager 收到所有确认,标记该 Checkpoint 成功。 这个过程称为 **Chandy-Lamport 算法** 的变种。 --- ## ✅ 如何配置持久化的 Checkpoint? 要让 Checkpoint 实现真正的“持久化”,必须配置以下两个方面: ### 1. 启用并配置 Checkpointing ```java StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // === 启用 Checkpoint === env.enableCheckpointing(5000); // 每 5 秒触发一次 Checkpoint // === 高级配置项 === env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().setCheckpointTimeout(60000); // 超时时间 env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500); // 两次间隔最小时间 env.getCheckpointConfig().setMaxConcurrentCheckpoints(1); // 并发数 env.getCheckpointConfig().setTolerableCheckpointFailureNumber(3); // 允许失败次数 env.getCheckpointConfig().enableExternalizedCheckpoints( ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION ); // 关键!保留 Checkpoint ``` 📌 `ExternalizedCheckpointCleanup` 参数说明: | 枚举值 | 行为 | |--------|------| | `DELETE_ON_CANCELLATION` | 取消作业时自动删除 Checkpoint(默认) | | `RETAIN_ON_CANCELLATION` | 取消作业后仍保留 Checkpoint(推荐用于生产) | 👉 如果你不设置 `.RETAIN_ON_CANCELLATION`,那么一旦你手动停止作业,Checkpoint 数据就会被删除! --- ### 2. 设置状态后端(State Backend)和 Checkpoint 存储路径 Flink 支持多种状态后端,它们决定了状态如何存储以及 Checkpoint 写往哪里。 #### 示例:使用 `FileSystemStateBackend`(推荐) ```java // 设置状态后端为基于文件系统的持久化存储(如 HDFS, S3, OSS) env.setStateBackend(new HashMapStateBackend()); // 指定 Checkpoint 持久化位置(必须是分布式可靠存储) env.getCheckpointConfig().setCheckpointStorage("hdfs://namenode:9000/flink/checkpoints"); ``` 或使用更现代的方式(Flink 1.15+): ```java env.setStateBackend(new HashMapStateBackend()); env.getCheckpointConfig().setCheckpointStorage( new FileSystemCheckpointStorage("hdfs://namenode:9000/flink/checkpoints") ); ``` ✅ 支持的存储路径协议: - `hdfs://...` — Hadoop HDFS - `s3://...` 或 `s3a://...` — Amazon S3 - `oss://...` — 阿里云 OSS - `gs://...` — Google Cloud Storage - `file://...` — 本地文件系统(仅测试用,不推荐生产) > ⚠️ 注意:`file://` 不具备高可用性,如果节点宕机,状态丢失! --- ## ✅ Checkpoint 目录结构示例 当你配置了 `hdfs://namenode:9000/flink/checkpoints` 后,Flink 会生成如下结构: ``` /flink/checkpoints/ ├── <job-id>/ │ ├── chk-1/ │ │ ├── ... │ │ └── _metadata │ ├── chk-2/ │ └── completed/ │ └── chk-3/ <-- 成功完成的 Checkpoint │ └── shared/ │ └── taskowned/ └── latest-flink-checkpoint-pointer -> chk-3 # 指向最新成功 Checkpoint ``` 这些数据包含了: - 算子状态(Operator State) - 键控状态(Keyed State,如 ValueState, ListState) - 分布式快照元信息 --- ## ✅ 如何从持久化 Checkpoint 恢复作业? ### 方法一:从 Savepoint 恢复(推荐用于升级/迁移) 虽然 Savepoint 和 Checkpoint 不同(Savepoint 是手动触发、格式稳定),但你可以先将 Checkpoint 转为 Savepoint: ```bash # 查看最新的 Checkpoint ID curl http://<jobmanager-address>:8081/jobs/<job-id>/checkpoints # 触发 Savepoint(可选) bin/flink savepoint <job-id> hdfs://namenode:9000/flink/savepoints ``` 然后重启作业并指定恢复路径: ```bash bin/flink run -s hdfs://namenode:9000/flink/savepoints/savepoint-abc123 -d job.jar ``` ### 方法二:自动从外部化 Checkpoint 恢复 如果你设置了: ```java env.getCheckpointConfig().enableExternalizedCheckpoints( ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION ); ``` 那么可以这样恢复: ```bash bin/flink run -s hdfs://namenode:9000/flink/checkpoints/<job-id>/chk-<N> job.jar ``` ✅ 这样即使作业被取消,也可以重新提交并继续从上次断点恢复。 --- ## ✅ 生产环境最佳实践 | 建议 | 说明 | |------|------| | 使用 `RETAIN_ON_CANCELLATION` | 防止误删 Checkpoint | | 将 Checkpoint 存储在 HDFS/S3/OSS 等分布式文件系统 | 保证可靠性 | | 定期备份重要 Checkpoint | 防止误操作或存储损坏 | | 避免使用 `MemoryStateBackend` 或 `file://` | 无法真正持久化 | | 监控 Checkpoint 成功率和耗时 | 使用 Flink Web UI 或 Prometheus + Grafana | --- ## ✅ 示例完整代码(Java) ```java public class CheckpointedStreamingJob { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // --- 状态后端 --- env.setStateBackend(new HashMapStateBackend()); // --- Checkpoint 配置 --- env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().setCheckpointTimeout(60000); env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500); env.getCheckpointConfig().setMaxConcurrentCheckpoints(1); // --- 持久化 Checkpoint --- env.getCheckpointConfig().enableExternalizedCheckpoints( ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION ); // --- 设置 Checkpoint 存储路径 --- env.getCheckpointConfig().setCheckpointStorage( "hdfs://namenode:9000/flink/checkpoints" ); // --- 数据处理逻辑 --- env.addSource(new FlinkKafkaConsumer<>("input-topic", new SimpleStringSchema(), properties)) .keyBy(value -> value) .map(new StatefulMapFunction()) .addSink(new FlinkKafkaProducer<>("output-topic", new SimpleStringSchema(), producerConfig)); env.execute("Stateful Streaming Job with Checkpoint"); } } ``` --- ## ✅ 常见问题排查 | 问题 | 解决方案 | |------|----------| | `ClassNotFoundException: org.apache.hadoop.fs.FileSystem` | 缺少 Hadoop 客户端依赖,添加 `hadoop-client` JAR 包 | | Checkpoint 超时或失败 | 检查网络、磁盘 IO、状态大小;增加超时时间 | | Checkpoint 太慢 | 减小状态大小、启用增量 Checkpoint(RocksDB) | | 恢复时报 `Incompatible checkpoint state` | 算子结构变化导致不兼容,需调整 UIDs 或使用兼容模式 | --- ## ✅ 增量 Checkpoint(高级功能,基于 RocksDB) 如果你的状态很大(GB 级别),建议使用 `RocksDBStateBackend` 并启用增量 Checkpoint: ```java RocksDBStateBackend rocksDBStateBackend = new RocksDBStateBackend("hdfs://namenode:9000/flink/checkpoints", true); env.setStateBackend(rocksDBStateBackend); ``` ✅ 优点: - 只上传变更的部分; - 显著降低 Checkpoint 时间和带宽消耗; ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值