摘要:数据到达速度超过处理速度,就会堆积、OOM、延迟失控。反压(Backpressure)就是解决这个问题的——观察处理延迟,反过来动态调整拉取速度。这篇把反压背后的 PID 控制器原理讲清楚:P、I、D 三个参数各自起什么作用,反压是怎么闭环收敛的,以及它到底能解决什么、解决不了什么。
关键词:Spark Streaming, 反压, Backpressure, PID 控制器, maxRatePerPartition, 限流
一、反压解决什么问题
先明确场景:当处理速度 < 数据到达速度,数据就会在内存里越积越多,最终 OOM 或延迟失控。
反压的思路不是"让处理变快"(那靠加资源),而是反过来——根据处理速度,动态降低拉取速度,让系统在一个稳定的速率上运行。它本质上是一个闭环控制:观察处理延迟,反推该拉多快。
二、PID 控制器:反压的核心

Spark Streaming 的反压用的是经典的 PID 控制器(比例-积分-微分)。闭环流程是这样走的:
公式长这样:
新 rate = 旧 rate + Kp·error + Ki·∫error + Kd·d(error)/dt
三个参数各管一件事:
- P(比例,Proportional):响应当前误差。延迟越大,降速越猛。默认 1.0。
- I(积分,Integral):响应历史累计误差,用来消除稳态误差(让延迟稳定在目标附近而不是一直偏着)。默认 0.2。
- D(微分,Derived):响应误差变化趋势,起预测、抑制震荡的作用。默认 0.0,一般保持不动。
三、反压的两个边界

反压不是万能的,有两个边界必须认清:
边界一:收敛有延迟
PID 控制器要经过多个 batch 才能收敛到稳定速率,不是一开就立刻生效。所以反压开启后的前几个 batch,数据仍然可能堆积。这就是为什么还要配一个 initialRate(初始 rate)兜底。
边界二:解决波动,不解决能力不足
反压能做的,是让拉取速度匹配处理速度——处理慢,就拉慢点。但如果处理能力本身就不够(业务逻辑重、资源不足),反压只能帮你降速避免崩溃,吞吐上不去是必然的。根本解决还是要加资源、加并行度、优化处理逻辑。
别指望开了反压,原本跑不动的作业就变得能跑了。
四、完整配置
spark.streaming.backpressure.enabled = true
# PID 参数(默认值已适合大多数场景,别乱调)
spark.streaming.backpressure.pid.proportional = 1.0 # P
spark.streaming.backpressure.pid.integral = 0.2 # I
spark.streaming.backpressure.pid.derived = 0.0 # D
# 关键:反压收敛前的初始 rate
spark.streaming.backpressure.initialRate = 10000
# rate 下限,防止降过头
spark.streaming.backpressure.pid.minRate = 100
两个最容易忽略的:
- initialRate:反压收敛前用的初始 rate。设太低,前几个 batch 拉得慢;设太高,收敛前就堆积。要按你实际的吞吐估一个合理值。
- minRate:rate 的下限。设成 0 的话,极端情况下反压可能把 rate 压到 0,流直接停摆。
五、反压 vs 手动限流
手动限流(只设 maxRatePerPartition)是固定一个上限,简单,但没法应对流量波动——峰值时处理不过来,谷值时又浪费了处理能力。
反压是动态调整,能自适应波动。但如前所述,它收敛慢,初期需要兜底。
所以生产上的最佳实践是两者结合:开反压负责动态调整,同时设一个合理的 maxRatePerPartition 作为上限兜底(也作为反压收敛前的初始约束)。
六、总结
- 反压解决"处理跟不上"导致的堆积,本质是闭环控制——观察延迟,动态调 rate。
- 核心是 PID 控制器:P 响应当前误差、I 消除稳态误差、D 预测趋势。
- 两个边界:收敛有延迟(前几个 batch 仍会堆积,需 initialRate 兜底);只解决波动不解决能力不足。
- 生产实践:开反压 + 设合理 maxRatePerPartition 兜底,别指望反压解决处理能力问题。
作者:大数据技术实践者 博客:blog.starzy.cn GitHub:starzy1990.github.io 专注 AI Agent · LangGraph · RAG · 大数据架构 · 数据工程实践






