欢迎光临
我们一直在努力

Flink 背压排查一眼看懂 Back Pressure,快速定位任务瓶颈

一、什么是 Flink 背压?

先说一句最容易记住的话:

背压不是“下游慢了”这么简单,而是“上游生产得比下游消费得快,导致数据推不出去”。

在 Flink 里,数据是从上游流向下游的:

  • Source → Map → Window → Sink

但是背压传播的方向,恰恰是反过来的:

  • Sink 慢
  • 会反压上游算子
  • 上游再继续反压更上游
  • 最后你可能在 Source 上看到 HIGH

这就是为什么很多人第一次看 Flink 背压会误判:

“Source 背压 94%,是不是 Source 有问题?”

其实不一定。
更有可能是:

  • 下游某个窗口算子处理慢
  • 某个 Sink 写出慢
  • 某个分支卡住了
  • 某些 key 特别热,导致部分 subtask 堵死

也就是说:

看到哪个算子背压高,不代表问题就出在它自己身上,它可能只是“被下游拖累”的受害者。

二、Flink 是怎么判断“背压”的?

Flink 内部判断背压,并不是靠“感觉”,而是有明确标准的。

其核心逻辑是:

  • 如果一个 task 没有可用的输出缓冲区(output buffers)
  • 那么这个 task 就会被认为是 back pressured

而 idle(空闲) 的判断则是:

  • 当前没有可处理的输入

换句话说:

  • Back pressured:有数据,但发不出去
  • Idle:没数据可处理
  • Busy:正在干活

三、最关键的 3 个指标:backPressured / idle / busy

Flink 每个并行实例(subtask)都会暴露 3 个核心指标:

  • backPressuredTimeMsPerSecond
  • idleTimeMsPerSecond
  • busyTimeMsPerSecond

它们的含义分别是:

1)backPressuredTimeMsPerSecond

这个 subtask 在最近一段时间里,有多少时间处于“被背压”状态。

2)idleTimeMsPerSecond

这个 subtask 在最近一段时间里,有多少时间在“等活干”。

3)busyTimeMsPerSecond

这个 subtask 在最近一段时间里,有多少时间真正处于忙碌处理数据状态。

这 3 个值加起来,大约等于:

  • 1000ms

也就是说,它们本质是在描述:

一个 subtask 在最近 1 秒时间里,分别有多少时间在“被堵住”“没活干”“忙着处理”。

这个设计特别好,因为它把任务状态拆得非常直观。

比如:

  • 100% / 0% / 0%
    说明:一直被背压,几乎完全堵住了

  • 0% / 100% / 0%
    说明:一直空闲,压根没数据

  • 0% / 0% / 100%
    说明:一直满负荷工作,但没有被堵

  • 50% / 50% / 0%
    说明:一半时间被堵,一半时间在等,没有真正处理太多数据

四、Flink Web UI 上颜色是什么意思?

Flink 在 JobGraph 里会把背压和忙碌程度做成颜色热力图,这一点对排障特别友好。

颜色大致可以这样理解:

  • 蓝色:空闲(Idling)
  • 黑色 / 深色:完全背压(Fully Back Pressured)
  • 红色:完全忙碌(Fully Busy)
  • 中间状态会用不同深浅混合表示

所以你在图上看到某个节点颜色特别深,并且背压值很高,往往就说明:

  • 它正在被下游明显限制

五、背压状态怎么分级?

在 Web UI 的 Back Pressure 页签中,Flink 会给每个 subtask 一个状态:

  • OK:0% <= back pressured <= 10%
  • LOW:10% < back pressured <= 50%
  • HIGH:50% < back pressured <= 100%

这个分级特别适合快速扫一眼现场:

  • 一大片 OK:整体比较健康
  • 零星 LOW:有轻微压力
  • 一片 HIGH:基本可以确认瓶颈已经比较明显了

六、先看第一张图:JobGraph 一眼看出谁在“拖后腿”

在这里插入图片描述

图1:Flink JobGraph 背压热力图,直观看出不同算子的 backpressured / busy / watermark 状态

从这张图里,我们能直接看到几个非常关键的现象。

1)Source 背压高达 94%

左侧 Source 节点显示:

  • Backpressured (max): 94%

这说明 Source 绝大多数时间都在“想发数据但发不出去”。

但这并不意味着 Source 自己有问题,更大的概率是:

  • 下游消费不动
  • 背压一路反传到了 Source

2)KeyedMapper 节点也有明显背压

中间这个节点显示:

  • Backpressured (max): 68%
  • Busy (max): 33%

这个状态很典型:

  • 它不是完全空闲
  • 也不是一直在忙
  • 而是很大一部分时间都在被堵

这通常意味着:

它有数据,也有处理能力,但输出链路不畅。

3)上方分支基本健康

上方这条分支:

  • TumblingWindowOperator:背压 0%,忙碌 38%
  • MapReturnsOutputWithCustomStatefulTypeSerializer:背压 0%,忙碌 13%

这说明上方分支不是主要瓶颈。

4)下方 SlidingWindowOperator 更像热点区域

下方这个节点显示:

  • SlidingWindowOperator
  • Backpressured (max): 20%
  • Busy (max): 82%

这个组合很有意思:

  • 忙碌度很高
  • 同时存在一定背压

这类节点非常值得重点怀疑,因为它往往意味着:

  • 它确实在高负载处理
  • 但处理速度已经开始跟不上了
  • 或者它后面的链路也存在压力

5)最重要的判断:问题大概率不在上面,而在下方链路或更后面的消费端

结合整张图来看,可以得出一个非常有经验味道的结论:

  • Source 背压很高
  • 中间 mapper 背压明显
  • 上方分支无明显背压
  • 下方 sliding window 分支更忙、更可疑

所以这时候真正应该重点查的,不是 Source,而是:

  • 下方 SlidingWindowOperator
  • 或者它后面的 Sink
  • 或者这条分支上的 key 分布是否不均衡

换句话说:

上游“喊疼”,不代表病灶在上游。真正的病灶,往往在更下游。

七、再看第二张图:SubTask 级别的背压明细,直接暴露不均衡问题

在这里插入图片描述
图2:Back Pressure SubTasks 明细页,不同 subtask 的 backpressured / idle / busy 差异一眼看穿

这张图比 JobGraph 更有价值,因为它把问题直接拆到了 subtask 级别。

页面顶部显示:

  • Back Pressure Status: HIGH

说明这个算子的整体背压已经很明显。

但真正值得看的,是下面每个 subtask 的具体状态。

1)Subtask 0、1、7、8、9、10:100% 背压

例如:

  • 100% / 0% / 0%
  • 状态:HIGH

这说明这些 subtask 几乎一直处于:

  • 有数据
  • 想往下游发
  • 但完全发不出去

这是非常严重的堵塞信号。

2)Subtask 2、3、4:0% 背压,100% 空闲

例如:

  • 0% / 100% / 0%
  • 状态:OK

这就特别有意思了。

同一个算子下,居然有些 subtask:

  • 完全被堵死

而另一些 subtask:

  • 完全没活干

这通常说明了一个非常典型的问题:

负载分布极不均衡。

最常见原因包括:

  • keyBy 后 key 分布严重倾斜
  • 某些热点 key 全部打到了少数 subtask
  • 某些分区数据远高于其他分区
  • 自定义分区策略不均衡

3)Subtask 5:49% 背压,51% 空闲

这是一个非常有代表性的“中间态”:

  • 它既不是完全健康
  • 也不是完全堵死
  • 说明它处在临界状态

这种 subtask 往往是判断问题边界的好线索。

4)Subtask 6:97% 背压

这说明它几乎和那些 100% 的 subtask 一样,也被严重压住了。

八、这张 SubTask 图到底说明了什么?

如果只看整体状态 HIGH,你只能知道“有问题”。

但看完 subtask 明细后,你能知道的问题就完全不一样了:

结论 1:这不是“所有 subtask 一起慢”

因为有些 subtask 是:

  • 0% / 100% / 0%

说明它们根本没在干活。

结论 2:这是非常明显的“局部拥堵”

有些 subtask 被压到 100%,有些却完全空闲,这说明瓶颈不是平均分布的,而是集中在少数并行实例上。

结论 3:高度怀疑数据倾斜 / 热 key / 分区不均

这类现象在 Flink 里特别常见于:

  • keyBy 后热点 key 集中
  • 某些窗口 key 特别热
  • 某些分区数据远大于其他分区
  • 自定义分区器分布不均匀

也就是说,这已经不是简单的“算子慢”,而更像是:

少数 subtask 被打爆,多数 subtask 在围观。

九、为什么很多人排查背压,总是查错方向?

因为他们只会看“哪个节点黑了”,不会看“黑的是不是所有 subtask”。

这是两个完全不同的排查维度。

情况一:所有 subtask 都很忙、都有背压

这更像是:

  • 整体处理能力不足
  • 下游整体吞吐太低
  • 硬件 / IO / 状态后端 / Sink 能力瓶颈

情况二:只有少数 subtask 背压严重,其他 subtask 空闲

这更像是:

  • 数据倾斜
  • 热 key
  • 分区不均
  • 某些特定 key 路径特别重

而你这张图,明显更接近第二种。

十、看到背压后,正确排查顺序是什么?

很多人一看到背压就开始:

  • 调并行度
  • 调 checkpoint
  • 调 buffer
  • 调内存

这不是不行,但很容易“头痛医头,脚痛医脚”。

更推荐你按下面这个顺序排。

第一步:先看 JobGraph,确定背压传播方向

也就是先看:

  • 谁最黑
  • 谁最忙
  • 哪条链路最可疑

你的图里很明显:

  • Source 被压得最明显
  • 但更可疑的是下方 sliding window 分支

第二步:进入 Back Pressure 页签,看 SubTask 明细

这是最关键的一步。

如果你不看 subtask,就永远无法判断:

  • 是整体慢
  • 还是局部热点

你的图里很明显是:

  • 局部热点严重

第三步:结合上游分区方式和 keyBy 逻辑排查

这一步往往才是真正的根因定位点。

重点问自己几个问题:

  • 是否做了 keyBy?
  • key 的分布是否均匀?
  • 是否存在热点 key?
  • 是否某些窗口 key 极度集中?
  • 是否分支逻辑导致一部分数据被路由到了少数 subtask?

第四步:再去看真正的业务算子是不是太重

如果 key 分布没问题,那再继续查:

  • 窗口聚合是否太重
  • 状态是否过大
  • Sink 写出是否慢
  • 外部系统(如 Redis / Kafka / ES / DB)是否跟不上

十一、一个非常实用的排障口诀

你可以直接把下面这段记住,基本够用了:

看到 Source 背压高

不要先怀疑 Source,先看下游。

看到某个算子 HIGH

不要只看算子级别,立刻下钻到 SubTask。

看到部分 subtask 100% 背压、部分 100% idle

优先怀疑:

  • 热 key
  • 数据倾斜
  • 分区不均

看到所有 subtask 都很高

优先怀疑:

  • 下游整体吞吐不足
  • Sink 慢
  • 状态后端慢
  • 外部系统瓶颈

十二、结合这两张图,给出一个更像“实战复盘”的结论

如果这是我在线上值班时看到的现场,我会先下这样的判断:

结论一:当前任务存在明显背压,且已经传导到了 Source

Source 的 Backpressured (max): 94% 说明背压传播已经非常充分,问题不是刚刚开始,而是已经比较明显了。

结论二:真正可疑的不是上方分支,而是下方 SlidingWindow 分支

因为上方分支几乎没有背压,而下方分支:

  • 忙碌度高
  • 存在背压
  • 更像瓶颈所在

结论三:SubTask 明细强烈暗示存在数据分布不均

部分 subtask:

  • 100% 背压

部分 subtask:

  • 100% 空闲

这不是“全链路慢”,而是“局部热点非常重”。

结论四:优先排查 keyBy 后的热点 key / 数据倾斜

这是当前最值得先下手的地方,而不是一上来就盲目加机器、加并行度。

因为如果根因是热点 key:

  • 并行度加了,也未必有用
  • 热点还是会继续落到少数 subtask 上

十三、背压优化,别只会“加并行度”

很多文章一谈优化就一句话:

  • 加并行度

这当然有时有效,但并不是万能药。

更靠谱的优化方向通常有这些:

1)先解决数据倾斜

如果是热点 key:

  • 优化 key 设计
  • 做 key 打散
  • 引入两阶段聚合
  • 局部预聚合后再全局汇总

2)优化下游重算子

如果某个窗口、聚合、序列化逻辑特别重:

  • 减少单条数据处理开销
  • 优化状态访问
  • 优化序列化
  • 避免不必要的对象创建

3)优化 Sink

很多背压最终都卡在 Sink:

  • Kafka sink flush 慢
  • 数据库写入慢
  • Elasticsearch bulk 慢
  • 外部服务吞吐跟不上

4)看状态后端和 checkpoint

如果有状态算子很重,也可能通过:

  • 优化 RocksDB 配置
  • 降低状态膨胀
  • 合理设置 checkpoint
  • 避免频繁大状态写入

来缓解背压。

十四、最后总结:真正会看背压的人,不会只盯着“HIGH”

很多人看 Flink 背压,只会看一个词:

  • HIGH

但真正有经验的人,会继续往下看两层:

第一层:是谁在被压?

也就是 JobGraph 上哪个节点在黑,背压是从哪条链路传上来的。

第二层:是全部 subtask 一起压,还是只有一部分在压?

这一步决定你是去查:

  • 整体吞吐瓶颈
    还是
  • 局部热点 / 数据倾斜

而你这两张图,已经非常典型地展示了一个线上高频场景:

上游 Source 背压很高,但根因更可能在下游某条分支,且高度怀疑热点 key 或数据倾斜。

这也是 Flink 背压排查最容易踩的坑:

看到 Source 高,就去查 Source。
其实 Source 很多时候,只是在替真正的瓶颈“背锅”。

赞(0)
未经允许不得转载:171主机测评 » Flink 背压排查一眼看懂 Back Pressure,快速定位任务瓶颈
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址