Flink 的 checkpoint 为什么越跑越慢

简介: 本文深入剖析Flink增量Checkpoint性能瓶颈:慢不在快照,而在SST文件上传。RocksDB Compaction导致旧数据重写为新文件,触发重复上传;状态分布不均加剧“短板效应”;小文件多、带宽竞争、引用计数延迟清理等问题叠加,使增量Checkpoint渐趋全量。核心在于理解“传什么(SST)”“为何重传(Compaction)”“谁在等待(JobManager同步等待所有subtask)”及“哪些指标会误导”。

很多 Flink 作业的 checkpoint 间隔设得很短,单次却跑得很久。

本地 RocksDB 刷盘是顺序写,快得多,剩下的大头都在把文件传到对象存储。

这就是这篇要拆的事。

checkpoint 越跑越慢,慢的不是快照,是上传。

先把结论放前面。

增量 checkpoint 传的也不是纯增量。

compaction 会把旧数据重新写成新文件,赶上合并的那一轮就得多传一遍。

对 exactly-once 来说,checkpoint 是刚需,绕不开。

所以问题不是要不要做,是怎么把开销压下来。

要讲清楚这件事,得先看增量 checkpoint 到底存了什么。

RocksDB 是 LSM 树,memtable 刷盘变成 SST,SST 一旦写完就不再改。

刷盘是本地顺序写,跟网络没关系。

慢的那一段,发生在文件离开本地盘之后。

checkpoint 触发时,Flink 让 RocksDB 把这一轮新产生的 SST 挑出来,只传这些,再附一份 metadata。

metadata 里不放数据,只放指向这些 SST 的引用。

目录结构不复杂,一个 job 一个目录,下面分 shared、taskowned 和 chk-N。

shared 放可能被多个 checkpoint 共用的文件,taskowned 放 JobManager 不能删、也不属于某一轮的文件,chk-N 放只属于这一轮的。

共用靠引用计数,认的是 state handle 的物理 ID,不是文件路径。

文件一旦被合并重写,物理 ID 就换了,引用计数也就认不出它是老文件。

所以增量 checkpoint 不是一份完整备份,是一条链,每一轮挂在前一轮后面。

一条链意味着一件事,快照做完,不等于文件已经传完。

增量 checkpoint 的快照阶段只决定这一轮要传哪些文件,真正的落地在异步上传里。

JobManager 收集每个 subtask 的 state handle,全部到齐,这一轮才算完成。

任何一个 subtask 上传翻车,整轮 checkpoint 就失败,上传这关绕不开。

在这里插入图片描述

这里还有个容易被忽略的前提。

增量 checkpoint 默认不开,得手动把 execution.checkpointing.incremental 设成 true,这是 2.3 的正名。

很多人以为用了 RocksDB 就自动增量,其实没有。

全量和增量的差别,不只是传多传少。

全量 checkpoint 写的是 Flink 自己的 key/value 快照格式,一份快照自带全部状态,换后端也能读。

增量传的是 RocksDB 的 SST,恢复的时候省掉了重建本地表那一步。

代价是恢复要顺着链把多个 delta 拉下来,链越长,拉得越多。

这也是为什么增量 checkpoint 的恢复时间,可能比全量还长。

savepoint 走的是另一条路。

它默认写成 canonical 格式,所以不能靠增量链。

要保留换状态后端的能力,就得接受全量写一遍。

canonical 格式的好处是跨后端可读,坏处是每次都要全量写。

链本身没问题,问题在 compaction。

RocksDB 会把小 SST 合并成大 SST,读得少,文件也少。

但合并出来的是新文件,文件名变了,身份也变了。

在 Flink 眼里,这就是一批从没见过的新 SST,得重新传一遍。

官方文档写得很直白,大多数增量状态后端都会周期性 compaction,结果就是把旧状态重新上传一次。

集群一大,几乎每个 checkpoint 里都有一个 task 在上传大量数据。

所以状态越大、更新越频繁、compaction 越勤,增量 checkpoint 就越像全量。

还有一层放大效应,来自数据分布。

状态是按 key 分到 subtask 的,key 一歪,某个 subtask 的 SST 就特别多。

compaction 也是按本地数据跑的,数据多的节点合并得更勤,重传得也更多。

于是每个 checkpoint 都有一两个重活节点,别的节点早就传完了。

在这里插入图片描述

上传还不只是总量的问题,是短板问题。

一个 checkpoint 要所有 subtask 都传完才算完成。

九十九个传得快没用,第一百个慢,整体就慢。

更麻烦的是排队。

文档里说得很清楚,checkpoint 跑得比间隔久,下一个不会提前触发,等当前完成之后立刻开始。

于是系统会一直处在 checkpoint 里,资源一直绷着。

execution.checkpointing.min-pause 能拉开一点距离,但只是把频率降下来,没解决单次为什么慢。

execution.checkpointing.max-concurrent-checkpoints 也不是解药,并发多了只会让上传互相抢带宽。

上传是异步的,但完成事件是同步等的。

JobManager 要等所有 handle 到齐才宣布完成,异步只说明它不挡算子处理数据,不代表完成判定可以提前。

把间隔拉长也不是免费。

间隔越长,两次 checkpoint 之间的 delta 越大,恢复点越旧。

RPO 和上传成本,本来就是跷跷板的两头。

指标也别只看一个。

duration 看总时长,size 看这一轮传了多少,full size 看总状态。

三个一起看,才知道是状态大了,还是这一轮改多了。

真正要看的,是单次 checkpoint 里,时间花在哪一段。

看两段就够了,对应 UI 里的 sync 和 async。

快照阶段长,多半是对齐或反压;上传阶段长,才是这篇文章要说的事。

还有一项开销,很多人根本没注意。

checkpoint 目录是会留下来的。

保留几份由 execution.checkpointing.num-retained 控制,每份增量都引用一批 SST。

引用计数没降到零之前,这些文件不会被删。

所以只要还在产生新 SST,共享目录就会一直长,引用计数归零只回收没人用的旧文件。

FLINK-30461 讲过一个极端场面,上传到一半超时,已经传上去的 SST 没被登记,共享目录里堆到上百万个文件。

这个 bug 在 1.15.4、1.16.2 和 1.17.0 都修了,但它说明了一件事,上传失败不是重来一次就能抹平的,是会留下垃圾的。

本地备份能救恢复,但救不了上传。

execution.checkpointing.local-backup.enabled 打开之后,TaskManager 的本地盘还会留一份状态副本,恢复时可以少拉远端。

但 checkpoint 该传的还得传,这份开销不会消失。

还有一个容易看错的数字。

开了增量之后,Web UI 里的 Checkpointed Data Size 就是刚才说的 size,只是这一轮的 delta,不是全量状态。

看到它很小就以为没事,其实总状态可能已经很大,只是这一轮刚好没怎么改。

真要看清,得看上传耗时、checkpoint 目录的增长速度、以及恢复时要拉多少数据。

在这里插入图片描述

恢复这事也有反直觉的地方。

网络带宽是瓶颈时,增量 checkpoint 恢复可能比全量更慢,因为要按链把多个 delta 都拉下来。

CPU 或本地 IO 是瓶颈时,增量反而快,因为不用按 Flink 的 key/value 格式重建本地表。

所以增量和全量之间,不是单纯的新比旧好,得看瓶颈在哪。

那怎么办。

我不太喜欢一上来就调间隔。

间隔被吃满,是上传慢的结果,不是原因。

先看三处。

第一处是重写。

compaction 越勤,重传越多。

能少更新、能提前把 key 打散、能让分布更稳,都是在减少重写。

第二处是上传。

本地盘和对象存储之间的带宽、并发、单文件大小,决定了这一轮要多久。

对象存储按请求计费,一个 SST 至少一次请求,文件越小,请求越多。

同一个目录下对象太多,list 也会变慢,清理和恢复都受影响。

小文件多了,光请求开销就够肉疼,让 SST 少而大,往往更划算。

大文件走分片上传,吞吐能上去,但并发和连接数又成了新的上限。

小文件太碎,在状态大的作业里很常见,调参折腾半天,到头来问题还在文件上。

第三处是换路。

ForSt 在 Flink 2.3 里还是实验特性,SST 直接放远端,天生就是增量。

好处是 checkpoint 时不用再整批搬一次。

代价是状态访问要过网络,延迟敏感的场景不一定合适。

但文档明确说不适合生产,只能当方向看。

三条路里,前两条是今天能动的,第三条是明年再看的事。

说实话,这套机制不算复杂,坑都在细节里。

传的是什么,为什么重传,谁在等谁,哪个数字会骗人。

这四个问题里,踩坑最多的是第一个,很多人根本不知道增量传的是 SST。

把这四件事拆开,checkpoint 慢在哪就清楚了。

checkpoint 的时间,最终都花在上传这一步。

本地快照再快,传不出去,这一轮就不算完。

Flink 2.3 的状态后端文档,增量那一节我翻了几遍,最要紧的就两句。

一句是默认不开,得手动打开;另一句是 compaction 会把旧状态重新上传。

这两句读懂了,剩下的都能自己推。

祝大家的 checkpoint,别卡在末尾那个 subtask 上。

相关文章
|
网络协议 Linux Android开发
告别无法访问的github(附解决方案)
最近一行在使用github的时候又登不上去了,挂着NPV都没用 据说是某些不可描述的有关组织机构对该网站的DNS污染或者随机丢包造成的
25523 5
告别无法访问的github(附解决方案)
|
7月前
|
存储 数据挖掘 OLAP
数仓入门篇-维度模型与第三范式
第三范式(3NF)是关系数据库规范化核心标准,要求消除传递依赖,确保数据无冗余、“一事一地”。维度模型则面向分析优化,采用星型结构与反规范化设计,以牺牲存储换查询性能与业务可读性。二者各适其用:3NF用于OLTP系统保一致性,维度模型用于OLAP场景提分析效率。(239字)
820 5
|
1天前
|
编解码 人工智能 JSON
用MCP做视频,怎样选生成模型?
从当前模型目录出发,按参考素材、声音锚、上一镜帧来源与输出规格筛选兼容方案,再核提示词到出片的字段交接与成功条件。
|
1天前
|
弹性计算 人工智能 网络安全
想买阿里云服务器,一年大概要花多少钱?价格划算吗?现在有没有折扣?附最新配置及报价
2026年的阿里云服务器市场,‌价格从来不是壁垒,信息差才是‌。阿里云轻量应用服务器38元一年的极致性价比、阿里云ECS云服务器99元起步的专业能力、阿里云GPU云服务器从入门到旗舰的完整算力矩阵——三条产品线各有千秋,覆盖了从个人开发者到超算集群的全部需求。 ‌核心建议只有一句话:先定业务,再定计费周期,最后匹配身份和优惠券。‌ 年付锁长期、月付保灵活、按量救急用、抢占省大钱。双十一大促在即,活动页上的价格随时在变——现在动手,正是最佳时机。
57 2
|
8月前
|
存储 分布式计算 Java
PySpark入门教程(非常详细)从零基础入门到精通
本教程聚焦Spark Core核心原理,基于3.5.8版本,用Python详解RDD五大特性(分区、计算函数、依赖关系、分区器、首选位置)、容错机制、Shuffle、DAG调度及共享变量等,并通过WordCount实战演示。
833 4
PySpark入门教程(非常详细)从零基础入门到精通
|
11天前
|
存储 SQL 缓存
Paimon把LSM搬进湖里的得与失
Paimon创新性地将RocksDB的LSM树引入湖仓架构,实现主键表的原生Update/Delete。通过分桶LSM、有序run和智能compaction,兼顾高吞吐写入与有界读延迟,但需精细调参平衡读写性能。(239字)
114 3
|
1天前
|
人工智能 5G
企业邮箱多少钱一年?阿里免费版0元、标准版600元一年、AI尊享版720元一年、国产化版900元一年
阿里企业邮箱提供免费版(0元/年)及三大付费版本:标准版600元、AI尊享版720元(原900元)、国产化版900元/年起(均5账号起)。支持50账号、不限容量、AI写信/润色/翻译等,满足不同企业需求。实时价格以官网为准。
|
1天前
|
人工智能 安全 搜索推荐
"冠军戒指" 钓鱼测试:内部演练的边界与安全启示
本文以MSG体育公司“总冠军戒指”钓鱼演练事件为切入点,剖析钓鱼攻击的社会工程学机理,反思内部安全演练的设计伦理与组织信任成本,提出需以“技术—人员—流程”协同构建纵深防御体系,并强调安全意识本质是可持续的组织文化,而非单次测试。(239字)
22 0
|
11月前
|
存储 人工智能 分布式计算
阿里云DLF 3.0:面向AI时代的智能全模态湖仓管理平台
在2025年云栖大会,阿里云发布DLF 3.0,升级为面向AI时代的智能全模态湖仓管理平台。支持结构化与非结构化数据统一管理,实现秒级实时处理、智能存储优化与细粒度安全控制,助力企业高效构建Data+AI基础设施。
2906 3

热门文章

最新文章