基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

在 Flink Web UI 上看到大面积的红色的“High”背压标识,你会按照怎样的步骤去寻找引起背压的真实瓶颈节点(是 Source 读太快、网络拥堵,还是 Sink 写太慢)?

知识点图片

在 Flink 中看到大面积红色的 “High” 背压(Backpressure),是一个非常经典的现象。

首先需要澄清一个 Flink 的核心概念:“Source 读太快” 从来不是引起背压的根本原因。Flink 采用的是基于信用值(Credit-based)的拉取网络模型,如果下游处理不过来,Source 自然会被“憋住”(显示为背压高),这是 Flink 保护自身的健康机制。

因此,真正的瓶颈永远在产生背压的最下游(或者说是背压传播的源头)

以下是我在生产环境中排查大面积背压问题的标准步骤:

第一步:顺藤摸瓜,定位“真正的”瓶颈节点

背压是由下游向上游传播的。当你在 UI 上看到一片红时,不要惊慌,按照数据流向(从 Source 到 Sink)去观察拓扑图。

  1. 观察 BackPressuredBusy 指标(Flink 1.13+ 提供):
    • 上游节点: BackPressured 很高(红色),Busy 较低。(因为数据发不出去,被迫空闲)。
    • 瓶颈节点: BackPressured 很低(绿色),但 Busy 极高(接近 100%,通常也是红色/深色)。
    • 下游节点: BackPressured 很低,Busy 也很低。(因为上游卡住了,下游处于“饥饿”状态,没数据可处理)。
  2. 结论: 找到那个 自己不背压,但非常忙碌(Busy),且紧挨着它的上游节点处于高背压 的 Operator。这个节点就是我们要开刀的“病灶”。

第二步:给瓶颈节点分类,排查具体原因

定位到具体的 Operator 后,我会根据该节点的类型和特征分情况排查:

场景 1:瓶颈在 Sink 节点(最常见的情况,“写太慢”)

如果定位到是 Sink 节点 Busy 极高,通常是因为外部系统(MySQL, ES, HBase, Redis, Kafka 等)写入慢导致的。

  • 排查动作:
    • 查看外部系统的监控(如 数据库的 CPU、磁盘 IOPS、连接数是否打满)。
    • 检查 Flink Sink 的配置:是否开启了批量写入(Batch Size / Flush Interval)?批量大小是否合理?
    • 是否使用了同步写入?(每次写入等待 ACK 极大地消耗吞吐量)。
  • 解决方向: 增大批量写入大小、改用异步 I/O(Async I/O)、增加 Sink 并发度、优化外部系统性能。

场景 2:瓶颈在中间处理节点(Window, Join, ProcessFunction)

如果瓶颈在中间的计算逻辑上,通常分为以下三种子情况:

A. 数据倾斜(Data Skew)—— 极其常见

  • 排查动作: 点开该瓶颈节点的 SubTasks 列表,查看各个并发实例的 Records ReceivedBytes Received
  • 现象: 如果发现某一个或少数几个 SubTask 处理的数据量是其他 SubTask 的几十倍,且只有这几个 SubTask 的 Busy 是 100%,那就是数据倾斜。
  • 解决方向: 对 key 进行加盐(Salt)打散、使用两阶段聚合(Local-Global Aggregation)。

B. CPU 计算密集 / 代码逻辑慢

  • 排查动作: 如果各个 SubTask 数据量均匀,但都很 Busy。我会直接使用 Flink Web UI 自带的 FlameGraph(火焰图) 功能。
  • 现象: 查看火焰图,看 CPU 时间消耗在了哪个具体的方法上。常见的罪魁祸首包括:复杂的正则表达式匹配、极其低效的 JSON 解析(如每次 new ObjectMapper)、死循环或耗时极长的业务逻辑。
  • 解决方向: 优化代码逻辑,或者单纯增加该节点的并行度。

C. 状态访问慢(RocksDB IO 瓶颈)

  • 排查动作: 如果节点用到了大量的 State(如大窗口、去重、CEP),且使用了 RocksDB 状态后端。
  • 现象: CPU 不算特别高,但 TaskManager 所在机器的磁盘 IOPS 打满(iostat 可见 await%util 极高)。
  • 解决方向: 将 RocksDB 目录挂载到 SSD 上;调整 RocksDB 的 BlockCache 和 WriteBuffer 参数;清理不必要的 State(设置 TTL)。

场景 3:网络拥堵(较少见,通常伴随资源瓶颈)

如果瓶颈节点的 Busy 并不高,但上下游之间的网络 Buffer 经常打满,可能是网络问题。

  • 排查动作: 查看 TaskManager 维度的 inPoolUsageoutPoolUsage 指标。
  • 现象: 机器的网卡流量监控跑满,或者跨机房传输导致的网络延迟。
  • 解决方向: 开启 Flink 的网络压缩(taskmanager.network.request-backoff.max 等优化),增加 Network Buffer 内存,或者梳理调度策略尽量让上下游 Task 调度在同一台机器上。

第三步:排除隐蔽的全局性干扰因素

如果以上步骤都没有找到明显瓶颈,我会检查两个全局性的“隐形杀手”:

  1. 频繁的垃圾回收(GC):
    • 排查: 查看 TaskManager 的 JVM 监控(Web UI 上的 Metrics 或接入的 Prometheus)。重点看 GarbageCollector 的耗时(Time)。
    • 现象: 发生了频繁的 Full GC(Stop-The-World)。JVM 停顿时,Task 无法处理数据,会导致瞬间产生严重的背压。
    • 解决: 分析 Heap Dump,优化对象的创建(复用对象),调整 JVM 堆内存大小。
  2. Checkpoint 阻塞:
    • 排查: 观察 Checkpoint 历史,看是否有某次 CP 耗时极长,或者对齐(Alignment)时间极长。
    • 现象: 在非 Barrier 对齐(Unaligned Checkpoint 未开启)的情况下,长耗时的 CP 会阻塞数据流,引发周期性的背压。
    • 解决: 开启增量 Checkpoint,考虑开启 Unaligned Checkpoint。

总结排查口诀:

先看 UI 找源头(高 Busy + 低背压),再看 SubTask 查倾斜,没倾斜看火焰图(抓 CPU),是 Sink 就查外部(抓 IO),最后别忘看 GC。

00:00
00:00