外观
Savepoint 升级 / 迁移作业
难度与前置
难度:架构
前置:运维过有状态 Flink 作业;读过 Checkpoint 与 Failover。
读完能做什么:用 Savepoint 做版本升级与扩缩容迁移,处理 uid/序列化兼容,并完成可回滚验收。
适用场景
- 作业逻辑小改(修 bug、加指标)需保留状态继续跑
- 扩并行度 / 换集群 / 换状态后端,需迁移 Keyed State
- 大版本 Flink 升级,需评估状态兼容与停机窗口
不适用:
- 无状态纯 ETL,停起即可,不必 Savepoint
- 状态语义已变(如键变了、窗口语义推翻)——应清空或重放,而非硬恢复
- 仅临时查问题用的 Derive 调试作业
真实场景(一条)
广告归因作业状态 800GB。升级代码改了 ProcessFunction 内部类名,未固定 uid,从 Savepoint 启动报 operator 不匹配,只能清空状态重放 3 天,SLA 打穿。事后:所有有状态算子固定 uid;CI 检查 uid 清单;升级演练「Savepoint → 新包启动 → 对比吞吐量/水位 → 保留旧包回滚点」。
怎么做(步骤清单)
1. Checkpoint vs Savepoint
| Checkpoint | Savepoint | |
|---|---|---|
| 目的 | 故障恢复 | 人为升级/迁移/停机 |
| 触发 | 周期自动 | 手动 / API |
| 保留 | 常增量、可清理 | 需显式保留路径 |
| 格式 | 后端相关 | 更强调可移植(仍有限制) |
升级迁移:先 Stop-with-Savepoint 或 savepoint 后 cancel,不要只依赖自动 CK。
2. 触发与启动命令(示意)
bash
# 触发 Savepoint
flink savepoint <jobId> hdfs:///flink/sp/attr_v2/
# 或停止并带走
flink stop --savepointPath hdfs:///flink/sp/attr_v2/ <jobId>
# 从 Savepoint 启动新包
flink run -d -s hdfs:///flink/sp/attr_v2/<trigger-id>/ \
-c com.example.AttrJob attr-job-2.3.0.jar \
--allowNonRestoredState # 仅当确认可丢弃已删除算子状态时3. uid 纪律(最高优先级)
java
DataStream<Event> s = env.addSource(src).uid("src-kafka-evt").name("src");
s.keyBy(Event::key)
.process(new AttrFn())
.uid("proc-attr-v1") // 逻辑不变则永不改;语义变则新 uid + 迁移方案
.name("attr");text
规则:
- 有状态算子必须显式 uid
- 禁止依赖自动生成 uid(拓扑微调即失效)
- 删除算子:评估 --allowNonRestoredState 或状态丢弃清单
- 新增算子:新 uid,旧 SP 无此状态属正常4. 兼容性检查清单
| 变更类型 | 可否从旧 SP 恢复 | 注意 |
|---|---|---|
| 修无状态 map | 通常可以 | uid 不变 |
| 改 Keyed State 结构 | 需 Avro/Pojo 兼容或 StateProcessor | 慎 |
| 改 keyBy 键 | 基本不可以 | 重放或自定义迁移 |
| 改并行度 | 可以(需 Max Parallelism 等约束) | 验证再均衡 |
| 换状态后端 | 需兼容路径 / 官方迁移工具 | 预演 |
| Flink 大版本 | 查发行说明 | 可能需两跳升级 |
5. 灰度与回滚
text
1. 旧作业 Savepoint,保留 jar 与 SP 路径 ≥ 回滚窗口
2. 新作业从 SP 启动,影子流量或双写对比
3. 观察:CK 成功、延迟、Lag、业务指标
4. 异常:停新作业,旧 jar + 原 SP(或升级前 CK)拉起
5. 文档记录:SP 路径、版本、允许丢失的 state 列表6. 扩缩容
- 目标并行度 ≤
max parallelism(创建状态时已定) - 扩容后关注状态重分布与短时反压(见 反压)
- 超大状态:优先增量 CK、本地恢复、分阶段扩
7. 与 Schema / 语义变更联防
状态里若存业务对象,序列化演进对齐 Kafka Schema 演进 纪律;破坏性变更走新 uid + 重放窗口,而不是硬从旧 SP 启。
常见坑(至少牢记这些)
不设 uid
一次重构类名就无法恢复,等于无状态资产。盲目
--allowNonRestoredState
静默丢窗口/累计值,指标「突然变准」其实是清零。Savepoint 路径被生命周期策略删掉
回滚时发现 SP 没了;要独立保留策略。改 keyBy 仍从旧 SP 启
状态键对不上,结果错乱或启动失败。大状态同步停机过久
未评估触发时长与对象存储吞吐,升级窗口不够。只升并行度不看 maxParallelism
启动失败或无法再均衡。
如何验收(可量化)
- [ ] 演练环境:Stop-with-Savepoint → 新包启动成功
- [ ] 有状态 uid 清单入库,CI 可 diff
- [ ] 启动后连续 N 次 Checkpoint 成功
- [ ] 消费 Lag、端到端延迟回到基线
- [ ] 业务对账:升级窗口前后关键指标连续(允许短暂抖动)
- [ ] 回滚演练:旧包可从保留 SP 拉起
bash
# 验收脚本示意
JOB=$(flink list -r | awk '/AttrJob/{print $4}')
flink savepoint "$JOB" hdfs:///flink/sp/drill/
# 记录 trigger id,启动新包后:
flink list -r
# 检查 Checkpoint Stats:最近 5 次 completed,failure=0sql
-- 业务连续性抽检(入仓明细)
SELECT dt, hour, COUNT(*) AS cnt, SUM(attr_amt) AS amt
FROM dws_attr_rt_hi
WHERE dt = '${biz_date}'
GROUP BY dt, hour
ORDER BY hour;
-- 升级小时与相邻小时量级连续,无断崖(结合业务峰谷)text
指标:
- checkpoint_duration_p99
- restart_count = 0(验收窗口内)
- kafka_consumer_lag 回落
- operator_state_size 与预期并行度匹配相关面试题
占位:与题库小程序对齐后补链接(建议关联:Savepoint vs Checkpoint、uid、状态兼容、作业升级回滚)。
改写说明:结构参考 Wood 原创 Flink 运维实践,已改写成可执行清单;未包含未公开集群路径与内部作业名。
附:发布检查单(可打印)
- [ ] 变更是否触及 keyBy / 窗口 / 状态结构?触及则单独迁移方案
- [ ] uid 列表已 diff,无意外改动
- [ ] Savepoint 保留 TTL ≥ 回滚窗口(如 7 天)
- [ ] 新包在预发用同日 SP 演练通过
- [ ] 回滚负责人与旧 jar 坐标已写进发布单