You need to enable JavaScript to run this app.
文档中心
文档控制台
注册
流式计算 Flink版

流式计算 Flink版

复制全文
下载 pdf
性能调优
Flink 任务内存调优最佳实践手册
复制全文
下载 pdf
Flink 任务内存调优最佳实践手册

文档背景

在云上使用流式计算 Flink 版时,内存配置是保障任务稳定性和性能的核心。相比于自建集群,云产品简化了集群管理,但如何为特定作业配置最优的内存参数,仍是用户需要关注的重点。不合理的内存配置会导致作业频繁 OOM(OutOfMemory)、性能瓶颈和资源浪费。
本文旨在为您提供一套在云上高效进行 Flink 内存调优的最佳实践,帮助您快速定位和解决内存问题,实现降本增效。

理解 Flink 集群中两个核心组件的作用是内存调优的基础。具体 Flink 的整体架构可以参考 Flink 官网提供的架构图:

JobManager (Master)

用途: 负责接收和提交作业(Job)、协调作业的执行(如调度任务、协调检查点 Checkpoint 和保存点 Savepoint)、故障恢复(Failover)等。它是整个集群的“大脑”。
内存需求: 通常不需要像 TaskManager 那样巨大的内存。其内存主要用于:

  • 管理 JobGraph 和 ExecutionGraph。
  • 记录完成的检查点元数据。
  • 与 TaskManager 的心跳通信。
  • 如果在 JobManager 上启用了 Web UI,也会消耗少量内存。

调优重点: 对于大多数作业,默认的 1C4G 内存通常足够。只有在管理极其庞大的作业(成千上万个任务)或保留大量历史检查点时,才需要考虑增加 JobManager 的内存 (比如 2C8G、4C16GB 等)。JobManager 总内存可遵循以下策略配置:

  1. 通过 Flink UI 进行查看作业 Task 总数
  2. 根据以下推荐内容进行 JobManager 资源设置

Task 总数

JobManager 资源建议

<100

0.5C2G

100-500

1C4G

500-1000

2C4G

1000-5000

4C8G

5000-15000

8C16G

15000

16C32G

TaskManager (Worker)

  • 用途: 真正执行数据计算任务(Task)的工作节点。每个 TaskManager 是一个 JVM 进程,负责运行用户代码、处理网络数据传输、状态存储等。内存调优的核心几乎全部集中在 TaskManager 上。
  • 内存需求: 需要大量内存,其分配非常复杂,是本文档讨论的重点。

TaskManager 内存分布

内存分布概览

自 Flink 1.10 版本之后,TaskManager 的内存布局发生了重大变化。了解下图和各个部分至关重要(此曲线可以通过任务运维 - Flink UI - TaskManager 进行查看)。

总内存 (Total Process Memory) = Flink 总内存 (Total Flink Memory) + JVM 元空间 (Metaspace) + JVM 额外开销 (Overhead)

内存详细说明

Flink 总内存 (Total Flink Memory): 由 Flink 框架直接控制和管理的内存部分。

  • JVM 堆内存 (Framework Heap + Task Heap): 经典的 JVM 堆区。

    • 框架堆内存 (Framework Heap Memory): Flink 框架本身运行所需的内存(如 RPC、协调等)。通常很小,无需调整。
    • 任务堆内存 (Task Heap Memory): 这是用户代码(如 MapFunction, ProcessFunction 中的逻辑)运行的空间。你创建的 Java 对象、用户定义的函数中的数据结构等都分配在这里。
  • 托管内存 (Managed Memory):

    • 用途: 由 Flink 直接管理(分配和销毁)的堆外(Off-Heap)内存。主要用于:
      1. RocksDB 状态后端: 这是最常用的部分。当使用 RocksDB 时,所有状态(包括算子状态和键控状态)都存储在这部分内存和本地磁盘上。
      2. 批处理作业的排序、哈希连接等操作: 为这些高内存消耗的操作提供内存空间。
      3. PyFlink: 用于 Python 进程的通信和数据交换。
    • 特点: 不受 JVM GC 影响,性能稳定,但需要自行管理。
  • 网络内存 (Network Memory): 用于任务之间数据传输的缓冲区(如输入/输出通道的缓冲区)。对于高吞吐量的作业,这部分内存至关重要。

  • JVM 元空间 (JVM Metaspace): 存储 JVM 加载的类信息等。与 Java 永久代(PermGen)类似,但位于本地内存。

  • JVM 额外开销 (JVM Overhead): 用于其他 JVM 开销,如线程栈、代码缓存等。这是为了确保整个进程不会因为一些“看不见”的内存使用而超出容器限制(如 Kubernetes Pod 的内存上限)而被 kill 掉。

常见的内存调优手段

调大 TaskManager 规格

  • 是什么: 这是最直接粗暴的方法——直接给 TaskManager 容器分配更多的内存和 CPU。
  • 何时使用:
    • 当作业出现 OutOfMemoryError 且你初步判断是总体内存不足时。
    • 当你希望提高单个 TaskManager 的能力,以运行更多任务槽(Slots)或更重的任务时。
  • 如何做: 在任务管理界面直接调大 TaskManager 的规格,避免使用 1C4GB 这种小规格容器,提升到 2C8GB / 4C16GB 等大规格容器,可以非常大幅提升整体 TaskManager 的资源容量。

调整 TaskManager 内部内存分布

这是最核心、最常用的调优手段。需要根据作业类型和问题症状来调整不同部分的大小。

调整任务堆内存 (User Code)

场景: 用户代码中创建了大量对象(如频繁创建的富对象、缓存大的哈希表/列表等),导致 java.lang.OutOfMemoryError: Java heap space
参数: taskmanager.memory.task.heap.size
做法: 增大此值。同时,应优化用户代码,避免数据倾斜、减少对象创建、使用轻量级数据结构。

调整托管内存 (State & Batch Ops)

场景 1 (流作业): 使用 RocksDB 状态后端时,性能瓶颈可能在于 RocksDB 需要频繁与磁盘交换数据。增大托管内存可以为 RocksDB 提供更大的块缓存(Block Cache),显著提升状态访问性能。

  • 参数: taskmanager.memory.managed.sizetaskmanager.memory.managed.fraction (相对于总 Flink 内存的比例)
  • 做法: 通常建议显式设置一个固定大小(如 2048m),而不是依赖比例。从 512MB 开始,根据状态大小和访问模式逐步增加。监控 RocksDB 的命中率指标。

场景 2 (流作业): 如果任务较为简单,没有大量使用状态的时候,可以通过减少托管内存,来为其他内存提供更多的空间。可以通过代码分析、或者 FlinkUI 中 Managed Memory 使用比例判断。

  • 做法: 减少步骤一中提到的参数值,通常根据状态大小和访问模式逐步减少,并且监控任务稳定性。

场景 3 (批作业): 进行大规模排序、连接操作时发生 OOM。

  • 做法: 同样增大此参数,为这些操作提供更多内存。

调整网络内存 (Data Transfer)

场景: 在高吞吐量作业中,任务间数据传输频繁。如果网络缓冲区不足,会导致背压(Backpressure),表现为下游节点处理慢,上游节点被迫降速。在 UI 上能看到明显的背压警告。
参数: taskmanager.memory.network.min, taskmanager.memory.network.max, taskmanager.memory.network.fraction
做法: 适当增大网络内存的上限(max)。Flink 会根据需求在 min 和 max 之间动态调整。

调整 JVM Overhead

场景: 任务进程被系统直接 kill 掉(从任务运行事件中可以看出),但 JVM 日志中没有明显的 OOM 错误。这通常是因为 JVM 使用的总内存(堆+栈+元空间+等)超出了容器限制,JVM Overhead 配置不足。
参数: taskmanager.memory.jvm-overhead.min/max/fraction
做法: 适当增大其最大值(max),例如从默认的 1G 增加到 2G,为线程栈等预留更多空间。

OOM 的识别、避免与处理

如何识别 OOM?
  1. 查看运行事件
  • Kill: 可能是 JVM Overhead 不足,进程被系统终止。
  1. 查看日志: 最直接的方式。搜索 OutOfMemoryError 关键字。常见的错误类型:
  • Java heap space: 任务堆内存不足。
  • Direct buffer memory: 网络缓冲区(属于堆外内存)不足。
  • Metaspace: 元空间内存不足。
  1. 监控指标: 利用 Flink UI 或任务详情 - 数据曲线查看:
  • Heap Memory Used 是否持续接近上限。
  • RocksDB Block Cache Hit Rate 是否很低(说明托管内存可能不够)。
  • 是否有持续的 背压 标志(黄色或红色),这可能间接由网络缓冲区不足引起。

常见原因及避免策略:
  1. 数据倾斜 (Data Skew)
    • 问题: 个别 Key 的数据量远远超过其他 Key,导致处理该 Key 的单个任务实例负载极重,内存消耗巨大,成为整个作业的瓶颈并最终 OOM。
    • 解决:
      • 检测: 通过 Flink UI 的每个任务的指标(如 Records Sent/Received)发现某个 SubTask 处理数据量远大于其他节点。
      • 加盐打散 (Salting): 将倾斜的 Key 加上随机前缀,先分散处理,最后再去盐合并结果。或者单独计算热点 Key 的结果,与其他 Key 计算结果 Union 到一起。具体文档可以参考 Flink SQL 数据倾斜处理最佳实践

Image

  1. 用户代码低效或内存泄漏

    • 问题: 在用户函数中无限制地缓存数据(如使用静态 Map),或创建了大量不必要的对象,导致 GC 频繁甚至 OOM。
    • 解决:
      • 使用 Flink 状态后端(ValueState, MapState等)来管理数据,而不是自己用 Java 集合缓存。
      • 优化代码逻辑,避免在循环中创建大量临时对象。
      • 使用 org.apache.flink.util.StringUtils 等 Flink 工具类,它们通常更高效。
  2. 缓冲区/超时设置不合理

    • 问题: 网络缓冲区(taskmanager.network.memory.buffers-per-channelfloating-buffers-per-gate)设置过小,在高并行度或高吞吐下无法有效缓冲数据,导致频繁背压甚至网络线程 OOM(Direct buffer memory)。
    • 解决: 除了调大总网络内存,也可以微调这些缓冲区的数量。通常让 Flink 自动调整即可。
  3. 大消息作业调优

    • 问题: Flink 默认的网络栈配置使用 32kb 的网络 buffer 大小。对于大消息(单条消息 > 32kb)作业,单条消息就需要拆到多个 buffer 里分别发送,导致网络传输侧的吞吐跟不上算子的处理吞吐,同时也会加剧同 TM 内算子间共用的浮动 buffer 池的挤占问题,出现上游算子反压严重,下游算子没打满的情况,同时也会出现 CPU 打不满、下游算子处理忙碌程度有倾斜等情况。建议将网络 buffer 大小设置为至少 2 倍消息大小。
    • 参数taskmanager.memory.segment-size,注意这个参数的值必须是 2 的指数次幂个 kb。
    • 解决: 例如最大消息可能是 372kb ,那么建议调整参数为 1024kb ,可以避免这种问题产生。
  4. RocksDB 配置不当

    • 问题: 虽然增大了托管内存,但 RocksDB 本身的配置(如 state.backend.rocksdb.block.blocksize)可能不匹配你的数据访问模式,导致缓存效率低下。
    • 解决: 查阅 RocksDB 性能调优指南,并通过 RocksDBOptions 进行自定义配置。

实战案例

TaskManager OOM Killed 退出

在 JobManager 日志中观察作业 Failover 原因是 TaskManager 失联、退出等。并在上下文中伴随着部分 OOMKilled 关键词。
这种情况是由于 TaskManager 进程使用的堆外内存超限导致被 OS/Kubernetes 直接杀掉进程。所以看不到具体的 Java 的异常栈。
针对这个问题,可以尝试直接增加堆外内存预留,具体参数如下,其默认值是 128M

taskmanager.memory.framework.off-heap.size: 512M

TaskManager 心跳超时

在 JM 日志中观察到 TaskManager 心跳超时退出,并 Task 由于 TM 退出触发 Failover。
这种情况需要检查 TaskManager/JobManager 是否存在比较严重的 GC,如果是的话需要增加 Heap 内存,可以考虑通过增加 CU 的方式来增加整体内存。

Direct Memory OOM

在 JM 日志中能直观的观察到 Task Failover 的异常栈中存在 OutOfMemoryError: Direct buffer memory异常关键词

这种情况需要增加 Task 的 OffHeap 内存,Flink 默认不会给 Task 预留堆外空间,共用 Framework 的 128M Direct Memory。

taskmanager.memory.task.off-heap.size: 512M

调优流程总结

  1. 监控先行: 首先通过 Flink UI 和日志确定问题大致方向(是堆、网络还是托管内存?)。
  2. 参数调整: 根据症状调整对应的内存组件参数。
  3. 代码优化: 检查是否存在数据倾斜或用户代码问题,这是治本的方法。
  4. 迭代验证: 每次只调整 1-2 个参数,然后观察作业运行情况,持续迭代优化。
最近更新时间:2025.09.04 11:30:15
这个页面对您有帮助吗?
有用
有用
无用
无用