最近帮人排查一个 Flink 1.13 作业频繁 Failover 的问题折腾到最后发现既不是业务逻辑的锅也不是数据倾斜而是 TaskManager 内存配置和系统内存打架容器直接被 OOMKilled。这个事让我意识到很多玩 Flink 的人对配置参数的了解停留在“能跑就行”出了问题根本不知道从哪个参数入手。我本人从 1.10 一路用到 1.13中间迁移过好几个实时集群踩过的配置坑比写过的业务逻辑都多。这篇东西不是官方文档的翻译而是把我在 1.13 上实际验证过、真的有用、且容易搞错的配置参数整理成一份工程笔记。如果你正在维护 1.13 集群或者打算给自己跑的实时作业做一次系统调优照着这份记录去核对应该能避开不少弯路。1. 先把配置体系撸清楚文件、命令行、代码谁说了算1.1 三处配置的生效顺序Flink 1.13 的配置来源大致有三个地方。第一是集群根目录下的conf/flink-conf.yaml这是所有作业的默认基准。第二是提交作业时通过-D指定的参数比如flink run -Dtaskmanager.memory.process.size2048m。第三是代码里通过Configuration或者ExecutionConfig动态设置的参数。三者的优先级很明确代码里设置的最高命令行-D次之flink-conf.yaml兜底。这个顺序好理解但实际中很多人搞反。我见过有同事在 flink-conf.yaml 里把重启策略配好了然后在代码里调setRestartStrategy用了另一套策略结果作业行为完全不是预期还以为是框架 bug。代码里覆盖配置是一把双刃剑调试起来特别麻烦因为你看不到作业到底用了哪个值。1.2 参数分类地图1.13 的配置项多到吓人粗略数有几百个但真正日常打交道的核心参数就几类。内存类解决“吃多少、怎么分配”并行度与 Slot 解决“任务怎么铺”Checkpoint 与状态后端解决“容错怎么做”网络与反压解决“上下游怎么协调”高可用与重启策略解决“挂了怎么办”。按这个地图去看参数你就不会在几十个以metrics.开头的配置项里迷失方向。说句大实话你不需要把所有参数都记住但每类的关键参数、默认值、调优方向应该门儿清。这样定位问题的时候脑子里会有一条清晰的排查路径而不是对着 Web UI 发呆。1.3 版本差异带来的坑1.13 这个版本比较特殊它是很多老 Flink 用户从 1.x 迁移过去的重要节点。它的配置体系大体延续了 1.11 以来的统一内存模型同时也新增了一些新特性开关。网上很多教程还在用老版本的参数直接复制过来跑能启动但实际没生效。比如 Checkpoint 相关参数1.13 统一推荐使用execution.checkpointing.*前缀早期一些文档里的flink.checkpointing.*写法已经不符合新版本规范。另外有些参数官方明确标注了“不建议修改”比如taskmanager.memory.managed.fraction这种不是不能调而是要清楚动了它会引发什么连锁反应。后文我会把这些关键差异点逐个讲清楚。2. 内存配置新手重灾区也是最先背锅的地方2.1 Flink 1.13 的内存模型1.13 沿用并完善了自 1.11 开始的统一内存模型。一个 TaskManager 进程的内存分为几块JVM 堆内存、托管内存Managed Memory、网络缓冲内存Network Memory、框架堆外内存和 JVM Overhead。这个模型的本质是框架不再让所有东西都往堆上塞而是把一些容易失控、容易引发 GC 问题的内存单独划出来管理。理解这个模型至关重要。我看到太多人直接给taskmanager.memory.process.size设一个总值然后就不管了。容器如果是 8G 内存他配 8G结果容器自己还有操作系统和其他进程要占内存直接被 Linux OOM Killer 干掉。这种问题在 Flink 里几乎成了新手第一大坑。2.2 核心内存参数速查表这些参数建议直接抄进你的flink-conf.yaml里然后根据实际业务场景微调参数名建议值说明taskmanager.memory.process.size实际容器内存的 75%~85%进程总内存包含堆、堆外、网络、托管等所有部分这是最常改的大头参数taskmanager.memory.framework.heap.size默认 128MB框架本身占用的堆内存业务代码不吃这块一般不动taskmanager.memory.task.heap.size默认值继承留给用户代码和状态的堆内存数据量大、算子状态多时考虑增加taskmanager.memory.managed.fraction默认 0.4托管内存占比RocksDB 状态会用到纯 DataStream 场景可适当调低taskmanager.memory.network.fraction默认 0.1网络缓冲内存占比Shuffle 数据量大时建议调大到 0.2 左右taskmanager.memory.jvm-overhead.fraction默认 0.1预留的 JVM 元空间、线程栈等开销值过小可能引发 OOM有个细节值得注意taskmanager.memory.managed.fraction默认 0.4 是针对所有状态后端设计的但如果你用的是堆上状态托管内存其实用不满。有些优化场景可以把 fraction 降到 0.2把省下的内存让给堆减少 GC 频率。但如果你用的是 RocksDB托管内存就是 RocksDB 的 Block Cache 和 Write Buffer 来源乱降会导致 RocksDB 性能大幅下降。2.3 实测踩坑容器 OOM 与内存口径真实案例是这样的。某公司有个作业TaskManager 容器开了 8Gtaskmanager.memory.process.size配了 8G跑了一个月后开始每隔几小时挂一次 TaskManager。排查发现容器被 OOMKilled原因很简单进程请求的内存上限是 8G而容器本身还有其他保留内存8G 已经是极限中的极限。后来把 process.size 降到 6G直接稳定了。这事的教训有两点。第一process.size永远不要等于容器内存上限要留出操作系统和监控 Agent 的生存空间第二看 Web UI 上的内存使用率时不要只盯着堆内存要关注整个进程的 RSS。Flink 的堆外内存和托管内存都是实打实从系统里拿的堆没事不代表进程没事。2.4 JVM 参数优先级的经验还有个常常被忽略的点直接改env.java.opts是能生效但它的影响是全局的。如果你的集群上同时跑多种作业有些作业需要大的堆外内存有些需要小的一刀切改这个参数会让某些作业白占资源。我个人的习惯是尽量不动全局 JVM 参数优先通过内存模型参数去控制。实在要调堆内大小就设置taskmanager.memory.task.heap.size让框架自己去分配。这样至少每个作业的内存结构是可预期的排查问题的时候少一层变量。3. Checkpoint 与状态后端容错的命脉3.1 基础 Checkpoint 参数表Checkpoint 参数是 Flink 容错的核心但很多人对它并不敏感直到作业大批量恢复失败才开始研究。其实核心参数就那么几个全列出来也不到一面墙参数名默认值我的建议execution.checkpointing.modeEXACTLY_ONCE需要高一致性时保持默认对吞吐有极致要求可改 AT_LEAST_ONCEexecution.checkpointing.interval无必须显式配置根据状态大小和恢复时间综合定常见 60s~300sexecution.checkpointing.min-pause0建议设为 interval 的三分之一左右给系统喘气机会execution.checkpointing.timeout10 min超过这个时间 Checkpoint 失败通常配合瓶颈排查execution.checkpointing.max-concurrent-checkpoints1不建议大于 2并发太多会拖垮背压execution.checkpointing.tolerable-failed-checkpoints0生产环境可设 3~5给短暂抖动留缓冲execution.checkpointing.aligned-checkpoint-timeout8s1.13 新特性超过该时间会自动切到 unaligned设置 Checkpoint 间隔的原则不是越小越好。如果状态有几十 G一个周期要处理很久间隔设得太短会导致上一个还没完成下一个又开始排队最终 checkpoint 延迟越来越大。我见过有人把 interval 设成 5s结果状态 20G每 5s 触发一次全量快照网络直接被拖爆。3.2 状态后端选型与 RocksDB 参数1.13 里默认状态后端还是堆上的 HashMapStateBackend适合小状态、追求低延迟的场景。状态一旦超过几个 GHeap 后端就会把 JVM 撑得很危险GC 停顿不忍直视这时候你需要换 RocksDB。RocksDB 状态下 Bulk 加载能力强状态存在堆外但性能受磁盘和内存配置影响很大。开启增量 Checkpointstate.backend.incrementaltrue很重要尤其是状态大的作业增量能省掉大量重复数据的上传。需要注意原生快照和增量快照的差异有些老的迁移教程里state.backend.rocksdb.memory.managedtrue并不是新版的默认1.13 中它默认就是 true也就是 RocksDB 内存跟随托管内存走。实操上我还遇到过一个诡异的坑给 RocksDB 配了托管内存但默认情况下它对 CPU 的使用也不低因为 compaction 就是 CPU 密集操作。你如果发现作业 CPU 高、延迟大先别急着骂业务代码看看是不是 RocksDB 在做 compaction。这时可以调整state.backend.rocksdb.memory.write-buffer-ratio适当降低写缓冲占比给读缓存更多空间经常能把延迟压下来。3.3 unaligned checkpoint1.13 的值得关注开关1.13 里对反压状态下的 Checkpoint 做了一个很有价值的改进unaligned checkpoint。当反压太严重对齐阶段迟迟收不到所有 input 的数据普通 checkpoint 就会超时失败。unaligned 方式是跳过严格对齐直接记录当前状态和输入缓冲里的数据这样能极大提高反压场景下的 Checkpoint 成功率。但要清楚它的代价Checkpoint 存储体积会变大恢复时需要回放的数据也更多。所以它不是银弹。我建议的用法是保留对齐超时机制也就是那个aligned-checkpoint-timeout让系统在正常时用对齐 Checkpoint真正遇到反压恶化时再退化到 unaligned。这种折中方案在实际生产中非常受用。3.4 状态 TTL 与清理很多从批处理转过来的同事会忽略 Flink SQL 有状态计算下的状态清理。配置上table.exec.state.ttl这一类参数控制状态数据的存活时间配合 RocksDB 的 compaction 过滤器能把过期数据在后台清理掉。如果你遇到状态无限膨胀、磁盘暴涨的问题第一反应应该是去看有没有配 TTL而不是盲目加内存。顺带分享一个排查心得状态膨胀往往不是单一原因而是 TTL 未设置、key 设计不合理、窗口开太大三个因素叠加。单独调大内存只能缓解症状治不了根本。4. 并行度、网络与反压吞吐和延迟的平衡艺术4.1 并行度与 Slot 的分配逻辑并行度参数常被简单理解成“越多越快”这是几乎所有调优事故的起点。并行度的提高意味着下游接收端增多中间需要 Shuffle 的数据量和网络连接数也在增加。如果网络缓冲内存没跟上并行度翻倍可能换来的是反压飙升。parallelism.default控制作业默认并行度taskmanager.numberOfTaskSlots控制单个 TaskManager 能执行的任务数。生产上我习惯把 Slot 数设为 CPU 核心数并保证每个 Slot 分到的内存不低于 1G。分配 parallelism 时永远记着一个公式总 Slot 数 TaskManager 数量 × 单 TM Slot 数并行度设置不能超过这个总 Slot 数否则任务排队等待资源可能直接卡在 RUNNING 状态出不来。4.2 网络缓冲参数反压的隐性开关反压是 Flink 世界里经常挂在嘴边的词。很多人一看到反压就怀疑业务处理慢其实有相当一部分反压是网络缓冲内存不足导致的数据链路堵塞。网络内存的核心参数是taskmanager.memory.network.min、max和fraction。默认 fraction 是 0.1大多数场景够了但高 Shuffle 量的作业比如 200 个并行度做一次大宽表关联网络缓冲很容易顶到上限。那时候你去看 Web UI下游 Subtask 的 receive buffer 一直是满的就算 CPU 很闲数据也送不进去。我的调法很简单先看反压是否持续如果持续且 CPU 没满就去 TM 的 metrics 里看 network buffer 使用率使用率超过 80% 再动手调 fraction。不要在系统正常的时候乱加网络内存因为它是从总内存里抠的加到别的模块没内存用反而会引发新的瓶颈。4.3 数据延迟与缓冲区心态Flink 为了吞吐会故意在发送端攒一批数据再发这个攒批的时间由 buffer timeout 决定。默认是 100ms 左右不同版本有差异如果你追求低延迟可以调小它但代价是每秒会产生更多小包网络吞吐下降。反过来如果是大吞吐、对延迟不太敏感的场景调大 buffer timeout 能减少网络调用次数一定程度提升吞吐。我记得第一次调这个参数时把延迟从 120ms 降到 30ms但吞吐掉了近两成。后来想了想这个作业的目标本身就是“秒级延迟可以数据量要大”于是又把值调了回去。调参这事必须基于自己的业务目标来做不要看着别人配什么就配什么。5. 高可用、重启策略与生产兜底5.1 重启策略别让你的作业裸奔Flink 的任务失败后怎么恢复由 restart strategy 决定。1.13 提供三种常用策略固定延迟fixed-delay、失败率failure-rate和不重启none。如果不设置默认依赖提交时是否开启 Checkpoint有可能出现作业失败就直接失败的惨状。参数名说明restart-strategyfixed-delay / failure-rate / nonerestart-strategy.fixed-delay.attempts失败后最多重试次数生产建议 3 以上restart-strategy.fixed-delay.delay两次重启之间的间隔建议 10s~60srestart-strategy.failure-rate.max-failures-per-interval时间窗口内最大失败次数restart-strategy.failure-rate.failure-rate-interval失败率统计窗口时长restart-strategy.failure-rate.delay失败后重试延迟固定延迟策略简单粗暴适合大多数作业。失败率策略更适合那种时不时抖动一下、但不想无限重启的作业。我个人强烈建议配合上一节说的tolerable-failed-checkpoints使用这样即使 Checkpoint 因偶发网络抖动失败作业也不会立刻走向重启。5.2 高可用配置要点生产环境不可能只有一个 JobManager需要配置高可用模式。1.13 支持三种none、zookeeper、kubernetes。核心参数是high-availability、high-availability.storageDir和集群标识high-availability.cluster-id。storageDir 非常重要Checkpoint 元数据和 JobGraph 都存这里如果这个目录丢了再好的 HA 也恢复不了作业。这里有个经常被忽视的点cluster-id不设置时默认是/default多个作业共用同一个 cluster-id 时会导致状态串扰。我排过一次事故两个作业强行共享 cluster-id结果重启一个把另一个的作业元数据也带崩了那种现场排查起来极其痛苦。所以强烈建议每个生产作业都显式指定独立的 cluster-id。5.3 日志与调试参数问题排查离不开日志。1.13 的日志配置主要通过log4j.properties或logback控制。想要定位序列化异常或者 RocksDB 问题可以把org.apache.flink.runtime.state的日志级别临时调到 DEBUG但不要长期开否则日志量大到能撑爆磁盘。另外flink-conf.yaml里rest.bind-port和web.access-control-allow-origin这些虽然不直接和业务相关但排查 Web UI 打不开、API 调用被跨域拦截的时候会用到。这类参数往往是最容易卡住人的因为它不属于主流调优话题遇到问题才想起来查。6. 常见问题排查实录与避坑速查6.1 现象一TaskManager 进程频繁被杀如果 TM 进程反复消失第一件事先看容器内核日志确认是否 OOMKilled。如果是大概率是taskmanager.memory.process.size和容器内存上限太接近。先把 process.size 降到容器上限的 80% 以下再看系统其他进程占用。如果还不行把jvm-overhead.fraction从 0.1 调到 0.15给 JVM 元空间和线程栈多留点余地。6.2 现象二Checkpoint 一直超时失败Checkpoint 连续失败别急着改 Checkpoint 参数先看反压。反压是 Checkpoint 超时最核心的原因数据发不出去barrier 也传不下去。此时需要先把反压源头解决了再回头看 Checkpoint。如果反压实在解决不了再考虑 unaligned checkpoint 或者把 interval 拉大。我遇到过一次奇特的情况Checkpoint 失败但反压状态正常后来发现是状态后端磁盘 IO 被某个共用目录写满快照文件持续写不进去。所以说排查要有顺序资源、网络、存储、代码。不要一上来就怀疑业务逻辑。6.3 现象三反压高但 CPU 很低这个现象最多见于数据倾斜或算子并发设置不合理。某个 Task 在接收端成为单点瓶颈上下游都在等它CPU 自然不高。先看 Web UI 里各个 Subtask 的繁忙程度分布如果某个 Subtask 明显高于兄弟 Subtask基本可以判定是 key 倾斜或者 shuffle 策略问题。不要把所有问题都甩给网络缓冲。6.4 我自己的排查工具链排查问题时我会用这么一套流程。第一打开 Flink Web UI看 Jobs 概览里的 State Size 和 Checkpoint 历史第二进到 TaskManager 的 metrics 标签页看taskmanager.Network.Memory.Used这类指标第三通过 REST API 拉取指定 job 的 metrics 做连续观察。命令一般是curl http://jobmanager:8081/jobs/xxx/metrics?gettaskmanager.Network.Memory.Used。这种看指标的方式比单纯盯日志高效得多能快速定位是内存、网络还是算子问题。症状优先排查方向关键指标TM 被杀内存配置 vs 容器上限container OOMKilled进程 RSSCheckpoint 超时反压、存储 IO、网络缓冲checkpoint durationnetwork buffer usage反压高但 CPU 低数据倾斜单点瓶颈busy 比例分布recordsInPerSecond状态无限膨胀TTLkey 设计窗口时长state size 曲线最后再分享一个小习惯我用 Flink 这几年最大的感悟是配置参数这种东西不能靠“感觉”去调。每次改参数之前先记录当前值、为什么改、期望效果改完之后再对比实际效果。我已经不止一次发现同一套参数在不同作业里的表现差很多因为每个作业的吞吐、状态、并行度、资源水位都不一样。从 1.13 这个版本往后Flink 的很多配置项也在逐步收敛和统一。如果你现在还在跑旧版本可以考虑在不影响大版本的前提下先把flink-conf.yaml里过期的参数名清理一遍然后用新版推荐的参数名重新核对。别总守着“能跑”的代码不放配置这关躲不过去早点消化它你的集群反而会越跑越顺手。