背压在 Flink 里不算罕见。WebUI 上一片红色,Lag 往上爬,Checkpoint 越来越慢,最后要么超时失败,要么结果延迟到业务受不了。概念大家都会说「下游处理不过来,向上游反压」,真正耗时间的是:谁在拖、为什么拖、能不能用局部手段扛住。
下面用一次真实一点的订单宽表任务,把排查路径和改法写清楚。
案例背景
任务大致链路:
Kafka(order_event)
-> JSON 解析 / 字段清洗
-> 维表异步 Join(用户、商品、店铺)
-> 窗口聚合(1 分钟)
-> 写入 JDBC upsert(结果库)
并行度 Source 12、中间算子 12、Sink 6。平时高峰 QPS 约 1.2 万,端到端延迟 3~8 秒。某次大促预热当晚,消费 Lag 半小时内从几千冲到 800 万+,Checkpoint 从稳定 8 秒变成频繁超过 1 分钟,最后超时失败重启,重启后又迅速背压。
业务侧表现:大屏订单量和实付金额明显滞后。
先看现象,再下结论
不要一上来改并行度。先固定看这几个位置:
- WebUI BackPressure
哪个 SubTask 长期 HIGH / OK 交替,注意是「从哪个算子开始变红」。 - 火焰图 / Task 指标
busyTimeMsPerSecond、idleTimeMsPerSecond、backPressuredTimeMsPerSecond。 - Checkpoint History
对齐时间、同步时长、状态大小是否突然变大。 - Kafka Lag 分分区
是全局堆积,还是某几个 partition 单点。
这次现象很典型:
- Source 的
backPressuredTime很高,但busyTime不高 → Source 本身不慢,是被下游堵住。 - 维表 Join 算子 busy 接近打满,异步客户端 P99 从 20ms 飙到 400ms+。
- Sink 也有背压,但幅度小于 Join;窗口算子状态大小当晚涨了一截。
结论初步指向:维表服务变慢是主因,窗口状态膨胀和 Sink 写入是放大器。
根因拆解
当晚维表(用户画像服务)做了一次扩容滚动发布,连接池短暂打满,超时重试被调高。Flink 侧 AsyncDataStream.unorderedWait 的超时是 3 秒,队列容量 1000。超时一多:
- 异步请求堆积,in-flight 打满。
- Join 算子处理不过来,向上游背压。
- Source 读 Kafka 变慢,Lag 上升。
- 事件时间窗口关不掉的数据更多,状态变大,Checkpoint 变重。
- Checkpoint 变重进一步占 IO / CPU,形成正反馈。
另外发现一个次要问题:Sink 并行度只有 6,JDBC 批量 batchSize=500,高峰时写入线程经常等待连接。主因不在 Sink,但它让恢复期更难「泄洪」。
治理动作(按优先级)
1. 先止血:降载 + 保护下游
临时把维表超时从 3s 调到 800ms,失败走侧输出写「待补全」Kafka,主链路先用默认值顶上。大促期间完整性可以后补,延迟不能继续恶化。
SingleOutputStreamOperator<OrderWide> joined = AsyncDataStream.unorderedWait(
cleaned,
new UserDimAsyncFunction(dimClient),
800, TimeUnit.MILLISECONDS,
2000 // 适当加大容量,避免动辄反压
);
joined.getSideOutput(DIM_MISS_TAG)
.sinkTo(dimMissKafkaSink);
同时把结果 Sink 并行度临时提到 12,batchInterval 从 200ms 调到 100ms,加快泄洪。
2. 再治本:维表与缓存
维表服务恢复后,在 Flink 侧加了本地 LRU + 短 TTL(例如 60s)缓存热点用户。大促下单用户重复度高,缓存命中后异步调用量下来一大截。
注意:画像字段若要求强一致,TTL 不能太长;本次业务接受分钟级延迟,60s 可接受。
3. 控制窗口状态
检查发现 allowedLateness=2min 在大促期过长,叠加乱序,窗口状态峰值到平时 3 倍。把 lateness 收到 30s,迟到数据进侧输出异步修补。Checkpoint 大小回落到可接受范围。
4. 资源与并行度对齐
Sink 与中间算子并行度对齐到 12;TaskManager 网络缓冲按官方建议略调高(taskmanager.memory.network.fraction),避免短暂抖动就触发反压。没有证据前不要盲目加 TM 数量,先确认瓶颈算子。
怎么判断背压「治好了」
可以看一组对比:
| 指标 | 故障时 | 治理后高峰 |
|---|---|---|
| Kafka Lag | 800万+ | < 5万,可消化 |
| Join 算子 busyTime | ~1000ms/s | 600~750ms/s |
| Checkpoint 时长 | 经常 >60s | 8~15s |
| 端到端延迟 | 数分钟 | 10s 内 |
另外约定告警:backPressuredTimeMsPerSecond 连续 5 分钟 > 500,或 Lag 连续上涨超过阈值,先翻维表延迟和 Sink 写入 RT,而不是先扩容 Flink。
小结
背压是结果,不是根因。这条任务的根因是维表变慢,窗口状态和 Sink 并行度放大了影响。排查顺序建议固定成:
WebUI 谁在背压 → 该算子 busy/idle → 外部依赖 RT / 超时
→ Checkpoint 与状态大小 → Kafka 分区是否倾斜
→ 先降载止血,再缓存/并行度/语义参数治本
下次再遇到「一片红」,先问:是算不完,还是等外部;是全局慢,还是单点 partition / 单点 key。这两个问题答清楚,方向就不会偏。