外观
Flink 反压怎么排查
难度与前置
难度:进阶
前置:能打开 Flink UI;知道 Source / 算子 / Sink、并行度、Checkpoint。
读完能做什么:用「先看颜色 → 再看指标 → 再定瓶颈」把反压定位到具体算子,给出可验收的缓解手段。
适用场景
当你发现实时作业 lag 持续上涨、Checkpoint 变慢或超时、端到端延迟飙升,UI 上出现 BackPressure 红/橙,需要系统化定位是「上游太快」「中间算子慢」还是「下游 Sink 堵」。
不适用:
- 刚上线、流量极小、尚未出现延迟问题(先把语义与 Checkpoint 跑通)
- 纯批离线任务(看 Spark/Hive 倾斜与资源即可)
- 已确认是 Kafka 分区本身无数据 / 上游停产(先排源,不叫反压)
真实场景(一条)
投放实时去重:Kafka → keyBy(bill_id) 去重 → JDBC Sink 写 MySQL。高峰期消费 lag 从 2 万涨到 80 万,Checkpoint 从 20s 拖到 8min 偶发失败。
UI:Source 与去重算子多为 OK/LOW,靠近 Sink 的算子持续 HIGH。JDBC 侧 batch.size 小、未开批量 upsert,单条写入打满连接池。
改法:Sink 改为批量 upsert + 合理并行;热点 key 做局部预聚合;确认后 lag 回落、Checkpoint 稳定在 1min 内。目标:反压消失且峰值 lag 可在 SLA 内消化。
怎么做(步骤清单)
1. 先统一「反压」定义
| 现象 | 含义 | 优先查 |
|---|---|---|
| UI BackPressure = HIGH | 该算子输出缓冲被下游「顶住」 | 该算子下游更慢 |
| 输入速率 >> 输出速率 | 处理能力不足或被阻塞 | CPU / 外部 IO / 锁 |
| Checkpoint 对齐很久 | 有通道长期不满或反压导致 barrier 卡住 | 反压热点 + 对齐模式 |
| Kafka lag 单调升 | 消费跟不上生产 | 整条链路吞吐瓶颈 |
TIP
反压颜色标在「被压住的一侧」。HIGH 的算子通常不是根因,根因在它的下游(更慢的那个)。从 Sink 往 Source 反查。
2. 用 UI 做五分钟初筛
- Overview:Job 是否 Running;重启次数是否异常
- JobGraph:从 Sink 往左看 BackPressure 颜色;记下第一个持续 HIGH 的算子及其直接下游
- Task Metrics(同一并行实例对比):
numRecordsInPerSecond/numRecordsOutPerSecondidleTimeMsPerSecond(偏高 = 在等输入)busyTimeMsPerSecond(接近 1000 = CPU 打满)backPressuredTimeMsPerSecond(偏高 = 被下游压住)
- Checkpoint:最近成功耗时、失败原因(对齐超时 / 异步快照超时)
- Task Managers:CPU、GC、网络;是否单 TM 打满
3. 三分类定位(对照表)
| 类型 | 典型信号 | 处理方向 |
|---|---|---|
| Sink / 外部系统慢 | 靠近 Sink 反压;外部 DB/Kafka 延迟、连接池满 | 批量写、异步客户端、扩下游容量、降并行冲突 |
| 算子计算慢 / 倾斜 | 中间算子 busy≈1000;个别 subtask 特别忙 | 优化 UDF、RocksDB 调优、打散热点 key |
| Source 侧「假反压」 | Source 也 HIGH,但实际是整链被 Sink 顶住 | 仍先治最下游,不要先加 Source 并行度 |
4. 缓解手段(按优先级)
A. 下游写入
text
1. JDBC:改为批次 upsert(flush 条数 / 间隔可配)
2. Kafka Sink:提高 linger / batch.size;注意事务超时与 Checkpoint 对齐
3. 外部限流:下游 QPS 上限明确时,用异步 + 并发度匹配,而不是盲目加并行B. 倾斜与热点
java
// 热点 key 局部加盐再聚合(示意)
DataStream<Event> salted = events
.map(e -> e.withSalt(ThreadLocalRandom.current().nextInt(8)))
.keyBy(e -> e.saltedKey)
.process(new LocalAgg())
.uid("local-agg");C. 资源与并行
- 先保证瓶颈算子并行度与 Kafka 分区数合理匹配
- RocksDB:检查状态大小、block cache;避免每个记录都打爆磁盘
- 反压严重时慎开过大
max-concurrent-checkpoints
5. 配置与观测补强
yaml
metrics.latency.interval: 30s
taskmanager.memory.process.size: 4g值班记录模板:
| 字段 | 填写 |
|---|---|
| 发现时间 / 业务窗口 | |
| 持续 HIGH 的算子链 | Source → … → Sink |
| busy / backPressured 比值 | |
| Checkpoint 最近耗时 | |
| 外部系统延迟(DB/Kafka) | |
| 临时缓解与永久改法 |
6. 与 Checkpoint / 延迟的关系
- 反压会拉长 barrier 对齐 → Checkpoint 变慢或超时
- 盲目「调大 Checkpoint 间隔」只是掩盖,故障窗口变大
- Unaligned Checkpoint 可在强反压下加快对齐,但先确认版本与场景,不是默认开关
相关实践见 Checkpoint 与 Failover。
7. 容量规划一页纸
| 项 | 写法示例 |
|---|---|
| 峰值生产 QPS | 8 万条/秒 |
| 单 task 能力 | 压测得出 |
| 需要并行度 | ceil(峰值 / 单 task 能力) |
| Kafka 分区 | ≥ 并行度 |
| 反压告警 | BP > 30% 持续 5 分钟 |
| lag SLA | 峰值后 15 分钟内回落 |
常见坑(至少牢记这些)
看见 Source 红就加 Source 并行度
整链被 Sink 顶住时,加 Source 只会堆更多缓冲。从下游往上游查。用「平均速率」掩盖倾斜
整体 In≈Out,但某一个keyBy分区 busy=1000、其余空闲。要看 per-subtask。反压时狂调 Checkpoint 超时
超时调大后「看起来成功」,实际恢复重放量暴增。应先消反压。JDBC 单条写入 + 高并行
并行度上去后打爆连接池,反压更重。批量 + 连接池上限要一起设计。忽略外部系统本身的限流与锁
MySQL 热点行锁、Kafka 单分区、HTTP 限流都会表现为「算子反压」。先证明外部 P99。
如何验收(可量化)
合并前 checklist,全勾才算本篇实践落地:
- [ ] 故障单写清:瓶颈算子名、判定依据(指标截图或数值)
- [ ] 峰值窗口
backPressuredTimeMsPerSecond在治理后显著下降 - [ ] Kafka lag 在业务峰值后可在约定 SLA 内回落(例如 15min 内降到基线)
- [ ] Checkpoint 成功耗时回到目标区间(如 P95 < 2× interval)
- [ ] 热点 key / Sink 批量策略有配置或代码落点,可 Code Review
- [ ] 回归:人为打压下游时,告警能指向正确算子而非只报 lag
bash
kafka-consumer-groups.sh --bootstrap-server ${brokers} \
--describe --group flink_ad_spend_dedupsql
SELECT DATE_FORMAT(etl_ts, '%Y-%m-%d %H:%i:00') AS m,
COUNT(*) AS cnt
FROM ods_ad_spend_realtime
WHERE etl_ts >= DATE_SUB(NOW(), INTERVAL 30 MINUTE)
GROUP BY 1
ORDER BY 1;相关面试题
题库小程序链接待定;先用站内锚点占位。
- [ ] 题:反压故障单最少写清什么?(考察点:瓶颈算子名 + 判定依据)→ 占位:
#todo-interview-backpressure-ticket - [ ] 题:治理后看哪些指标回落?(考察点:backPressuredTimeMsPerSecond + Kafka lag SLA 内回落)→ 占位:
#todo-interview-backpressure-metrics - [ ] 题:Checkpoint 怎样算回到健康?(考察点:成功耗时进目标如 P95)→ 占位:
#todo-interview-backpressure-checkpoint - [ ] 题:人为打压下游时告警应指向谁?(考察点:正确算子而非只报 lag)→ 占位:
#todo-interview-backpressure-alert-target
改写说明:结构与判断标准参考 Wood 原创数仓/面试文,已改写成可执行清单;未包含未公开公司数据与内部表名。