☰
Flink 1.13 配置参数详解:内存、Checkpoint、时区与反压调优实践
2026/10/22 15:13:44 网站建设 项目流程

1. 为什么单独整理 Flink 1.13 的配置参数

接手 Flink 1.13 集群维护之后,我做的第一件事就是把 flink-conf.yaml 从头到尾翻了一遍。不是闲得慌,而是 1.13 这个版本在参数语义上确实动了不少:内存模型早不是 1.9/1.10 那套老字段,Checkpoint 相关配置统一成了execution.checkpointing.*前缀,SQL 的时区处理也多了table.local-time-zone。网上很多教程要么停留在老版本,要么直接丢一个"推荐配置"但不解释为什么,照着抄完,任务能启动,但行为不对,比如时区差八小时、Checkpoint 频繁失败、容器被 OOMKilled。所以我把实际维护中验证过的配置参数按主题做了一次整理,这篇主要写给正在用或者计划升到 1.13 的同学,尤其是负责作业稳定性、集群资源调优的人。

1.1 语义与默认值变化的不止是"几行配置"

先说一个最容易踩的坑:老教程里的taskmanager.heap.mb、taskmanager.memory.size这类写法,在 1.13 里已经不再承担原来的职责,统一被taskmanager.memory.*系列替代。如果还在老参数和心参数之间混着配,Flink 会以优先级更高的新参数为准,经常出现"我改了配置但没生效"的错觉。

再比如 Checkpoint 的配置,1.13 的官方推荐写法已经收敛到execution.checkpointing.*这一套:

早期常见写法1.13 推荐写法备注
checkpoint.intervalexecution.checkpointing.interval语义一样,前缀统一
checkpoint.timeoutexecution.checkpointing.timeout默认 10 分钟
checkpoint.max-concurrentexecution.checkpointing.max-concurrent-checkpoints默认 1
checkpoint.min-pauseexecution.checkpointing.min-pause-between-checkpoints默认 0
checkpoint.externalizedexecution.checkpointing.externalized-checkpoint-retention默认 NONE

如果你是从老版本直接升级,建议先在测试集群跑一遍全量作业,重点看flink-conf.yaml里是否还有废弃参数。1.13 对某些废弃参数仍然容忍,但语义已经和你想的不一样,这种"不报错但结果不对"的问题最头疼。

1.2 记参数不是在抄参数

我对参数记录的态度是:每一条配置都应该能回答三个问题——它解决什么问题、它影响了哪块资源、调错它会有什么现象。这篇里的参数都按这个思路来写,不是从官方文档复制一遍参数名,而是把我在实际运维中遇到的现象、排查链路和调整结果放进来。尤其是 1.13 之后,很多默认值比老版本更合理,但业务场景不同,合理的基线也不同,你要做的不是照搬一份配置,而是理解之后调出自己的基线。

2. 内存参数:先搞懂 TaskManager 的内存账本

内存参数是 Flink 配置里最容易出问题、也最需要先理清楚的一块。很多人直接把容器内存设成 8G,taskmanager.memory.process.size也写 8G,结果任务一跑起来就被杀掉,原因就是没留出容器本身的系统开销。要搞清楚这个问题,得先看懂 TaskManager 的内存账本。

2.1 总内存怎么推导出各分区

在 1.13 里,TaskManager 内存的核心关系是两个公式:

taskmanager.memory.process.size = taskmanager.memory.flink.size + JVM Overhead taskmanager.memory.flink.size = Framework Heap + Framework Off-Heap + Task Heap + Task Off-Heap + Network Memory + Managed Memory

process.size是向容器申请的总内存,flink.size是 Flink 自己管理的部分,JVM Overhead 用来覆盖 Metaspace、线程栈、JVM 自身开销和可能的 native 内存。如果你只设置了taskmanager.memory.process.size,Flink 会按比例推导内部各分区;显式设置某个分区大小时,显式值优先,剩余部分再按比例分摊。

这里有个容易忽略的点:taskmanager.memory.managed.fraction默认 0.4,也就是 Flink 总内存的 40% 会被划给 Managed Memory,用于 RocksDB、排序、窗口缓存等。如果作业状态不大,Managed Memory 占 40% 其实很浪费,反而挤压了 Task Heap。所以状态小的作业,我通常显式设置taskmanager.memory.managed.size而不是让它按比例走。

Network Memory 也不是固定值,它由taskmanager.memory.network.min、taskmanager.memory.network.max、taskmanager.memory.network.fraction三者共同约束。默认 fraction 是 0.1,min 是 64MB,max 是 1GB,实际取值会在 min 和 max 之间按照 fraction 计算。

2.2 常见内存配置模板

我根据作业类型整理了两套基础模板,都是flink-conf.yaml的写法。第一套是"大状态 + RocksDB"场景:

taskmanager.memory.process.size: 8192m taskmanager.memory.task.heap.size: 2048m taskmanager.memory.managed.size: 3072m taskmanager.memory.network.min: 256mb taskmanager.memory.network.max: 1024mb taskmanager.memory.jvm-overhead.fraction: 0.12

第二套是"纯计算、状态很小"的场景:

taskmanager.memory.process.size: 4096m taskmanager.memory.task.heap.size: 2048m taskmanager.memory.managed.size: 512m taskmanager.memory.network.min: 128mb taskmanager.memory.network.max: 768mb taskmanager.memory.jvm-overhead.fraction: 0.15

注意,我一般不会只给process.size然后听天由命,而是至少显式控制 Task Heap 和 Managed Size。因为默认的 0.4 比例在状态小的时候太浪费,在状态大的时候又可能不够,这两个分区是作业性能的关键。

2.3 容器 OOMKilled,别再只盯 heap

一个非常常见的现象:容器内存 4G,taskmanager.memory.process.size设 3G,作业运行一段时间后容器被杀,监控看到内存超过 4G。很多人第一反应是堆内存溢出了,但 GC 日志显示 Heap 完全健康。这通常是process.size没覆盖 JVM Overhead 以外的系统开销。

实际上容器里的 JVM 会占用 RSS 内存,其中包括堆、Metaspace、线程栈、Direct Memory、JNI 分配的内存,以及 JVM 自己分配但还未归还系统的部分。process.size里的 JVM Overhead 默认只有 0.1 比例,min 192MB、max 1GB,在内存大的机器上经常不够。我的经验是:如果容器可能被 OOMKilled,先把taskmanager.memory.jvm-overhead.size或者对应 max 值调大,同时给容器内存留出 10% 到 15% 的余量,别让process.size顶满容器内存。

提示:Kubernetes 部署时,除了 Flink 的内存,还要把监控 Agent、日志收集等 sidecar 的内存算进容器规格,最好单独留一份给它们,否则 Flink 自己跑得很稳,也会被共享内存挤爆。

3. Checkpoint 与状态后端参数:把"恢复能力"补上

Checkpoint 参数决定了作业故障之后能恢复到什么程度、恢复速度多快。1.13 里execution.checkpointing.*这一套已经成熟,RocksDB 状态后端也还是大状态场景下的主流选择,这几组参数值得认真配。

3.1 RocksDB 参数组:先选后端再谈状态

状态后端的核心参数是state.backend,生产上大状态基本都用 RocksDB。配上之后,还要想清楚几个配套项:

state.backend: rocksdb state.checkpoints.dir: hdfs://nameservice/flink-checkpoints state.savepoints.dir: hdfs://nameservice/flink-savepoints state.backend.incremental: true state.backend.local-recovery: true

state.backend.incremental建议在能用的场景下打开,RocksDB 增量 Checkpoint 只上传变更的 SST 文件,对带宽和 HDFS 压力小很多。state.backend.local-recovery默认是 false,打开它之后,恢复作业会优先读取本机緩存的 Checkpoint 状态,可以明显减少故障恢复时间。不过它也会多占磁盘,需要保证 TaskManager 节点有足够的本地盘。

还有一点,RocksDB 的内存由 Managed Memory 托管,当state.backend.rocksdb.memory.managed为 true 时,RocksDB 的 block cache 和 write buffer 都从 Managed Memory 里分配。这也是为什么状态大的作业必须把taskmanager.memory.managed.size调大的原因,RocksDB 给太少内存,读写性能会断崖式下跌。

3.2 调度与超时的参数组合

Checkpoint 经常被调的参数是 interval、timeout、min-pause 和 tolerable-failed。我给一份常用组合:

execution.checkpointing.interval: 60s execution.checkpointing.timeout: 120s execution.checkpointing.max-concurrent-checkpoints: 2 execution.checkpointing.min-pause-between-checkpoints: 30s execution.checkpointing.tolerable-failed-checkpoints: 3 execution.checkpointing.externalized-checkpoint-retention: RETAIN_ON_CANCELLATION

interval 是触发周期,timeout 是单次 Checkpoint 必须完成的时间上限。timeout 太短,网络抖动时容易失败;timeout 太长,Checkpoint 堆积会拖慢数据处理。min-pause-between-checkpoints 控制两次 Checkpoint 之间的最小间隔,避免过于频繁地做快照。

zài 设置 max-concurrent-checkpoints 时要注意:并发 Checkpoint 会同时读多个状态快照,对磁盘和网络压力更大。我一般保持 1 到 2,除非是 source/sink 延迟很低、状态很小的作业,否则不轻易拉高。

externalized-checkpoint-retention 建议设成RETAIN_ON_CANCELLATION。这样手动停止作业后,Checkpoint 数据不会自动清理,为后续恢复留个后路。至于 Savepoint,它的作用主要是版本升级和应用逻辑变更时使用,路径单独配置,别和 Checkpoint 目录混在一起。

3.3 Unaligned Checkpoint:1.13 里值得注意的有选项

1.13 里值得单独说的是execution.checkpointing.unaligned.enabled,开启后 Checkpoint 不需要等所有通道的对齐 barrier,直接记录当前缓冲区的数据。它本质是用更多网络内存和临时数据,换一个更短的 Checkpoint 完成时间。

execution.checkpointing.unaligned.enabled: true execution.checkpointing.unaligned.timeout: 0

当unaligned.timeout为 0 时,只要启用 Unaligned Checkpoint,所有 Checkpoint 都会走非对齐模式;如果设置一个大于 0 的值,则会先尝试对齐 Checkpoint,只有在超过阈值后才切换为非对齐。实测下来,在跨节点数据倾斜明显、Checkpoint 经常因为 barrier 对齐超时失败的作业上,开启 Unaligned 能让 Checkpoint 的完成时间明显更稳定。但这个功能会带来更高的内存占用,不算无代价优化。如果作业的 Checkpoint 本来就稳定,不建议全局开启。

4. Slot、并行度与调度参数:别让资源在角落里吃灰

内存之外最常见的资源瓶颈就是 Slot 和并行度。很多人以为 Slot 数等于并行度上限,实际他们之间的关系没有想象中那么死板,配置时要看算子链和 Slot 共享。

4.1 Slot 不是核,并行度也不是越多越好

TaskManager 通过taskmanager.numberOfTaskSlots定义 Slot 数量,parallelism.default定义作业默认并行度。Slot 不是 CPU 核,只是一个执行资源单元,一个 Slot 可以串行执行多个算子的 subtask,前提是它们属于同一个 Slot 共享组。

Flink 默认会把所有算子放进同一个共享组,所以一个作业即使有 source/task/sink 三个并行度,只要每个 TM 的 Slot 数足够多,一条链上的算子能共享 Slot。这也带来一个常见误区:并行度设得很大不代表真的能跑到那么大,只有当 Slot 总数足够,并且算子之间没有挤在一起时才有效。

我在规划并行度时,先看瓶颈算子的数据量,再反推 Slot 数。比如上游消息量平均 5 万条每秒,单并发处理能力如果是 1.5 万条每秒,那瓶颈算子至少需要 4 并发。然后加上 Sink 和 Source 的并发,再结合算子链接,估算需要的总 Slot 数。

4.2 和调度体验相关的几个参数

调度环节的几个参数,平时不起眼,但集群不稳时影响很大:

slot.request.timeout: 120000 cluster.evenly-spread-out-slots: true jobmanager.execution.failover-strategy: region

slot.request.timeout是请求 Slot 的等待时间,超过这个时间就报资源不足。小集群频繁提交作业时,如果这个值太低,会因为调度抖动直接失败。cluster.evenly-spread-out-slots打开后,会尽量把同一个作业的 Slot 打散到不同的 TaskManager 上,避免热点节点,但也增加跨节点数据传输,这里要看数据本地性需求来取舍。

jobmanager.execution.failover-strategy在 1.13 里默认是 region,区域故障恢复只重启受故障影响的那部分任务,而不是整个作业都重启。这对长作业非常重要,一个 TaskManager 挂掉时,没受影响的分区不会跟着中断。

4.3 一个简单的并行度规划示例

假设一个作业:Source 并行度 4、状态计算并行度 8、Sink 并行度 4,状态在 RocksDB 里,Source 和 Sink 与计算节点之间存在明显网络传输。默认 Slot 共享下,3 个算子链会被归到同一组,如果单个 TM 有 4 个 Slot,两个 TM 就能满足最大并行度 8。

但如果作业里某个算子手动设置了不参与共享(比如disableChaining或改了共享组),Slot 需求就会上升。我见过一个作业,因为某工具自动改了共享组,并行度没动,但需要的 TM 从 2 台涨到 4 台,资源占用翻了一倍。所以每改一次作业图结构,都要重新算一遍 Slot 需求,不能只看并行度。

5. 网络缓冲与反压参数:吞吐瓶颈往往藏在这一层

网络内存和反压是 Flink 运维里最容易被忽略、也最容易误导人的部分。上游产得飞快、下游处理不过来时,Web UI 上会显示反压警告,很多人的第一反应是"调大并行度",但真正的问题可能在网络缓冲配置上。

5.1 网络内存的分配逻辑

网络内存的分配由三件套控制:

taskmanager.memory.network.min: 128mb taskmanager.memory.network.max: 1024mb taskmanager.memory.network.fraction: 0.1 taskmanager.memory.segment-size: 32768

网络内存会被切成一个个 segment,每个 segment 默认 32KB。数据传输时,每条传输通道会分独占 buffer,剩余的浮動 buffer 由多个通道共享。细粒度参数taskmanager.network.memory.buffers-per-channel默认 2,taskmanager.network.memory.floating-buffers-per-gate默认 8。数据通道多、并发高时,独占 buffer 不够,就需要更多浮动 buffer,否则数据会在网络层排队,表现为上游反压。

5.2 反压观测参数

Flink Web UI 的反压监控本身也有几个参数可以调:

web.backpressure.refresh-interval: 60000 web.backpressure.num-samples: 100 web.backpressure.delay-between-samples: 1000

这些参数控制反压采样频率和样本数量。采样间隔越短、样本越多,反压状态越准确,但会给 JobManager 带来额外开销。如果总感觉 Web UI 上的反压状态变化不及时,可以先调refresh-interval,不用动业务流程。

5.3 实战中的 buffer 调优方向

遇到反压时,我的排查顺序是:先看瓶颈算子是否是 Sink,再看是否有数据倾斜,最后才怀疑网络参数。网络 buffer 不是越大越好:buffer 太多,内存被网络层占掉,Managed Memory 和 Heap 就会缩水;buffer 太少,通道等待时间变长,吞吐反而下降。

我遇到过的一个案例是作业并行度高、单 TM Slot 多,正常业务量下一切正常,峰值一来就反压报警,Checkpoint 也开始超时。看了监控发现网络内存频繁打满,把taskmanager.network.memory.max从 512MB 提到 1024MB,同时taskmanager.network.memory.buffers-per-channel从 2 提到 4,峰值时段的吞吐明显好转,Checkpoint 也稳定了。这不是万能解药,但对高并发通道数据倾斜不大的场景很有效。

6. 容易被忽略的"隐藏参数"

有些参数不像内存和并行度那样显眼,但配置错了,造成的后果非常诡异,比如时间差八小时、作业启动后任务一直 ATOMIC 失败但日志没报错、或者故障后重启策略和预期完全不同。

6.1 时区:SQL 里最容易"差八小时"的参数

1.13 的 SQL 引入了table.local-time-zone,这个参数决定 TIMESTAMP_LTZ 类型的显示和转换方式。如果不显式配置,它使用 JVM 默认时区,部署环境一变,时间就乱了。

table.local-time-zone: GMT+8

注意,table.local-time-zone影响的是整个会话的时间语义,需要在作业启动前就要确定。最稳妥的做法是统一在flink-conf.yaml里固定时区,不要在 SQL 代码里依赖本地机器的时区设置。这个参数我最早忽略过,上线后才发现所有按天分区的数据全部错位。

6.2 重启策略:决定故障后的命运

重启策略在 1.13 里由restart-strategy控制,它的默认行为和 Checkpoint 是否开启有关:如果没开启 Checkpoint,默认策略是不重启;开启 Checkpoint 后,默认是固定延迟重启。很多"任务挂了但没起来"的现象,根源就是没显式配重启策略。

restart-strategy: fixed-delay restart-strategy.fixed-delay.attempts: 3 restart-strategy.fixed-delay.delay: 10s

如果业务对抖动容忍度高,failure-rate策略更合适:

restart-strategy: failure-rate restart-strategy.failure-rate.max-failures-per-interval: 5 restart-strategy.failure-rate.failure-rate-interval: 10min restart-strategy.failure-rate.delay: 5s

failure-rate 允许在 10 分钟内最多失败 5 次,每次间隔 5 秒,超过阈值就彻底停止。相比 fixed-delay 的固定次数,它更灵活,但也需要根据作业的实际启动时长来调整 delay,避免还没启动完就再次重启。

6.3 类加载、临时目录与本地恢复

依赖冲突是 Flink 作业里特别常见的问题,典型的报错是 NoSuchMethodError。除了排查 jar 冲突,还应该看看classloader.resolve-order。Flink 默认对用户代码采用子优先加载,如果想优先让框架类不被用户 jar 覆盖,可以调整为父优先,或者通过classloader.parent-first-patterns.additional把特定包强制归到父加载器。

临时目录方面,1.13 推荐使用taskmanager.tmp.dirs和jobmanager.tmp.dirs,而不是旧版的io.tmp.dirs。本地恢复状态也会写临时文件,所以临时目录最好指向大容量本地盘,避免写满系统盘导致 RocksDB 写入失败。

7. 一套能直接对照的 flink-conf.yaml

参数列得再多,都不如一份能直接对照的模板来得实在。下面这份配置是我在 1.13 生产环境里用过一段时间的模板,场景是中等状态量、RocksDB 后端、消息队列上游、HDFS 做 Checkpoint。

7.1 生产中参考模板

# 内存 taskmanager.memory.process.size: 8192m taskmanager.memory.task.heap.size: 2048m taskmanager.memory.managed.size: 3072m taskmanager.memory.network.min: 256mb taskmanager.memory.network.max: 1024mb taskmanager.memory.jvm-overhead.fraction: 0.12 # 状态与 Checkpoint state.backend: rocksdb state.checkpoints.dir: hdfs://nameservice/flink-checkpoints state.savepoints.dir: hdfs://nameservice/flink-savepoints state.backend.incremental: true state.backend.local-recovery: true execution.checkpointing.interval: 60s execution.checkpointing.timeout: 120s execution.checkpointing.max-concurrent-checkpoints: 2 execution.checkpointing.min-pause-between-checkpoints: 30s execution.checkpointing.tolerable-failed-checkpoints: 3 execution.checkpointing.externalized-checkpoint-retention: RETAIN_ON_CANCELLATION # 调度与并行 taskmanager.numberOfTaskSlots: 4 parallelism.default: 4 slot.request.timeout: 120000 cluster.evenly-spread-out-slots: true jobmanager.execution.failover-strategy: region # 网络 taskmanager.memory.segment-size: 32768 taskmanager.network.memory.buffers-per-channel: 4 taskmanager.network.memory.floating-buffers-per-gate: 8 # 重启策略 restart-strategy: failure-rate restart-strategy.failure-rate.max-failures-per-interval: 5 restart-strategy.failure-rate.failure-rate-interval: 10min restart-strategy.failure-rate.delay: 5s # SQL 时区 table.local-time-zone: GMT+8

这套配置关键是显式控制了 Managed Memory 和 Network Memory,避免默认比例打架;RocksDB 使用增量 Checkpoint 和本地恢复;重启策略用 failure-rate,而不是放任默认行为。

7.2 上线前我必查的清单

每次作业上线前,我会按这个顺序核对配置:先看内存,确认process.size和容器规格之间有没有留出余量;再看 Checkpoint,interval、timeout、externalized 三者的数值是不是符合业务可接受范围;然后看 Slot 与并行度,算一下作业所需的 Slot 总数,确认集群剩余资源足够;最后检查时区和重启策略,这两个是"错了不会立刻报错,但运行一段时间后必出问题"的典型。

最后说一个我自己的习惯:把每个作业的最终配置提交到项目仓库里,而不是只在集群上改。因为 Flink 的配置排查,最难的是"当时改了哪个节点上的哪个文件",有了版本记录,回滚和复盘都简单很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询