Skip to content
Charles Shao
Go back

Bidder 预算 Pacing:PID 控制与消耗平滑

Updated:
–views

本文是 买侧竞价链路 系列的第 8 篇(预算 Pacing)。 全系列 9 篇:

  1. Bidder 竞价服务架构
  2. OpenRTB 协议精讲
  3. Bidder 解析层
  4. Bidder 定向过滤
  5. RTA 竞价前过滤
  6. Bidder 频次控制:五维配置与身份降级
  7. Bidder 频次控制工程篇
  8. Bidder 预算 Pacing
  9. Pacing 目标曲线

一句话定位:全面解析竞价引擎的预算 Pacing 机制,涵盖目标曲线设定、执行手段比对以及 PID 闭环反馈在分布式环境下的架构落地与调参实践。

防范超投仅仅是预算控制的合规下限。如果竞价引擎(Bidder)采取「预算充足即全力参竞、预算耗尽即停止投放」的粗放策略,往往会在流量高峰的上午时段将整日预算消耗殆尽,导致需求方平台(DSP)在下午和晚间的实时竞价(RTB)中彻底缺席。对于广告主(Advertiser)而言,这种前重后轻的消耗模式不仅会严重扭曲触达人群的时间结构,还会因高峰期的激烈竞争而大幅抬高单位获取成本。正因如此,我们需要引入 Pacing(平滑投放)机制。该机制的核心诉求不仅是控制总花费量,更要确保预算在整个投放时段内按既定节奏平稳释放。

从工程本质来看,Pacing 属于典型的反馈控制系统。平台首先需设定一条反映「当前时刻预期累计消耗额」的目标曲线,随后实时测量真实的累计消耗进度,并利用两者的偏差信号来动态调节底层出价的激进程度。为了完美解答这一闭环命题,工业界最普遍采用的核心组件便是 PID 控制器。

一天内累计预算消耗曲线对比图:横轴为投放时段 0~24 小时、纵轴为累计消耗占日预算百分比,带 0/25/50/75/100 网格线与图例;灰色虚线为目标曲线 r(t)(计划,从 0 线性爬升到 100%),红色实线为无 Pacing 的 ASAP 消耗(上午高峰陡峭上冲、第 9 小时撞到 100% 并有竖直虚线标注『第 9h 预算烧光』,之后一整天平摆、顶部有浅红阴影带标注『ASAP 撞顶后一整天停投:下午/晚间缺席』),绿色实线为有 Pacing 的实际消耗(紧贴灰色目标线平滑爬升、到第 24 小时刚好花完),另有『无 Pacing:高峰猛冲』『Pacing:匀速贴合目标』两处内联标注 累计消耗曲线展示了无 Pacing 的红线在上午高峰耗尽预算后全天停摆,而有 Pacing 的绿线则紧贴目标平滑消耗并精准收盘,Pacing 的核心价值便在于将预算均匀摊平至整个投放时段。

TL;DR

Table of contents

Open Table of contents

一、Pacing 要解决什么:不做会怎样

在程序化广告生态中,单个广告计划通常受到总预算与日预算的双重约束。竞价引擎除了需要防范单日超额花费,更需要直面时间维度的分配难题:即如何将既定的日预算在当天 24 小时内科学、合理地调度释放。

若系统完全褫夺 Pacing 机制,Bidder 将彻底退化为贪婪的 ASAP(As Soon As Possible)模式,即只要预算账户仍有结余且能够赢得当前竞拍,便无条件溢价参竞。这种盲目出价行为会导致整体消耗曲线被自然流量的起伏所裹挟,进而引发一系列不可挽回的业务灾难:

正因如此,防范超投仅仅是筑牢了不亏钱的合规堤坝,而 Pacing 机制则从根本上保障了预算释放的时间分布一致性。需要强调的是,时间分布的均衡度直接裁决了最终的人群触达纯度与全盘获取成本。与此同时,Pacing 也肩负着防范欠投的重任。如果在投放周期落幕时预算仍有巨额结余,则意味着广告主的真金白银与供给方平台(SSP)的广告曝光(Impression)机会惨遭双向流产。因此,Pacing 的终极诉求是实现实际消耗与预期进度的完美双向贴合,既扼杀提前透支,也杜绝无为的年终结余。

二、第一步:定义目标消耗曲线 r(t)

要实现精准的进度贴合,系统必须预先绘制一张清晰的参照蓝图。目标消耗曲线 r(t) 严格定义了「在任意时刻 t,累计消耗金额应占总体日预算的百分比比例」,它构成了整个反馈控制中枢的设定值(setpoint)。业内常见的曲线形态主要分为以下三类:

曲线类型r(t) 形态适用场景核心代价
ASAP阶跃至顶冷启动抢量、限时活动消耗前重后轻、推高买量成本
均匀(Even)随时间线性增长流量大盘趋于极度平稳高峰期错失低价量、低谷期耗速停滞
流量加权依托历史流量分布加权生产环境的主流首选极度依赖流量大盘的精准预测能力

在残酷的生产实践中,流量加权策略无疑占据着绝对统治地位。试想,若晚间 20:00 的可竞买流量库占据了全天总盘子的 10%,那么系统为这一小时分配的合理目标消耗增量就理应是预算的 10%,而非僵化地按时钟刻度进行均摊切分。从底层数学公式推演,目标累计占比 r(t) 实际上就是一个归一化的累计流量权重累加函数:

         Σ(τ ≤ t) w(τ)              w(τ) = τ 时段的预估可竞价流量(或流量 × 预估赢率)
r(t) = ──────────────────  ∈ [0,1]   分母 = 全天权重之和 → 保证 r(T) = 1(收盘恰好消耗完毕)
         Σ(all τ)  w(τ)

这种设计的绝妙之处在于,目标曲线本身能够柔软地顺应自然流量的潮汐脉搏。通过大幅缩减目标预期与实际流量供给的先验偏差,PID 控制器后续需要强力纠偏的阻力被降至最低,整个闭环系统也因此运转得更加丝滑。对比之下,均匀平铺策略仅仅是 w(τ) ≡ 常数 时的一种极其简陋的降级模型罢了。

必须澄清一个行业共识:计划的精细度决定了执行的天花板。目标曲线刻画得越贴近真实世界,底层控制器背负的维稳压力就越轻。众多平台爆发的 Pacing 恶性震荡,其病灶往往扎根于粗糙的流量预测管线,而绝非控制算法本身的残缺。

至于如何雕琢更为科学的权重 w(τ),即如何在纯粹的基础流量、综合的预估赢率与深度的转化价值之间进行调和博弈,本质上已跨入独立的模型预测领域。相关硬核解法已在姊妹篇 Pacing 目标曲线:时段消耗权重怎么设计 中作了详尽的推演。本篇后续将建立在「最优目标曲线已下发就绪」的假设前提下,全力聚焦下行的闭环调控逻辑。

此外,为了构筑系统的弹性护城河,r(t) 绝对不应被硬编码为僵硬的法币绝对值。更明智的架构规范是将其抽象为累计消耗比例。因为在复杂多变的真实投放环境中,日预算随时面临着临时修改或跨集群机房的动态再平衡。依托百分比抽象机制,一旦底座预算池发生剧变,高层的控制曲线便能以优雅的姿态实现自动无损拉伸。

三、第二步:两种执行手段——节流 vs 出价系数

当反馈中枢基于进度偏差下达了明确的加速或减速号令后,业务层需要配备相应的物理执行旋钮去实质性地阻尼或放水资金的流速。在竞价漏斗中,最核心的两把制动阀门便是节流与出价系数。

Pacing 两种执行手段对比图,上下两栏面板。上栏「手段 A · 节流(Throttling):只调参不参与、出价不变」——横向流程:控制器输出 u → 投放概率 p(rand() < p 才竞价)→ 出价保持不变 → 优点『实现简单·不扭曲赢率曲线』/缺点『无差别丢弃·被丢的无反馈样本』。下栏「手段 B · 出价系数(Bid Modulation):只调出多少、每次都参与」——横向流程:控制器输出 u → final_bid = base × α(α 随消耗快慢浮动)→ 每次都参与竞价 → 优点『连续可调·顺响应曲线更省预算』/缺点『需懂赢率-出价曲线·与 bid shading 耦合』。底部旁注:生产多以出价系数为主、节流为辅 对比节流与出价系数两种执行手段:节流通过调节投放概率 p 实现无差别拦截,而出价系数则通过动态折扣 α 顺应拍卖响应曲线,在保障系统平滑的同时进一步优化了单位买量成本。

3.1 节流(Throttling / Probabilistic Pacing)

节流堪称最为直接的干预手段:系统在面对每一次竞价请求(bid request)时,仅进行简单的离散概率抽样。只有当系统随机数 rand() < p 时,才允许该次请求下探至底层的预估与出价模块,其余请求则被直接丢弃。此时,控制中枢唯一调节的变量即为投放概率 p ∈ [0,1]。若消耗势头过猛则压低 p,反之则放宽限制。

3.2 出价系数(Bid Modulation / Bid-based Pacing)

对比前一种方案,出价系数策略要求系统接纳全量请求参竞。但在最终核算出价环节,它会叠加一个动态的 Pacing 乘子 α,计算公式为 final_bid = base_bid × α。遭遇消耗洪峰时,系统通过下调 α 致使最终出价降低,由此带来的直接后果便是竞得率萎缩与消耗节奏放缓;反之,若需拉升消耗,则抬高乘子以提升竞得率。

3.3 实践怎么选

维度节流(Throttling)出价系数(Bid Modulation)
调节旋钮投放概率 p ∈ [0,1]出价系数 α(final = base × α)
调节连续性离散抽样的粗粒度拦截连续变量驱动的平滑过渡
赢率分布干扰维持原貌(保全原生胜率)主动重塑(剔除高溢价请求)
单位成本控制表现一般聚焦低价流量,成本优势显著
模型反馈数据拦截导致训练集特征缺失全量参竞维系完整数据闭环
系统落地门槛实现简单,开箱即用架构复杂,存在响应曲线耦合风险

综合评估成本与收益后,业内头部变现中台通常采用「以出价系数为主、以节流为辅」的融合架构。在常规运作期,系统全权委托出价系数 α 进行主导,在保障买量成本的同时打磨消耗曲线的平顺度;而一旦发生极端流量爆发,导致 α 触底依然无法遏制消耗超速时,系统便会果断祭出节流概率 p 实施硬性拦截。

四、第三步:为什么是 PID——从开环到闭环

4.1 开环为什么不行

在构建初始的调速引擎时,研发人员极易陷入开环思维的误区:预先根据目标曲线计算出下一周期的精确消耗额度,随后机械地释放等额预算。然而,这种开环系统缺乏反馈机制,盲目假设只要系统下放预算,前端就必然能精准完成等量消耗。在真实的广告竞价环境中,这一假说极易失效:

面对上述环境扰动,开环系统缺乏自我修复与纠偏能力,只能任由实际消耗轨迹偏离既定航线。破局的唯一出路在于构建闭环控制:系统必须实时测量真实消耗,将其与目标值进行比对,进而将误差信号反向注入到底层的调节链路中,最终实现动态的自适应纠偏。

4.2 PID 是什么,为什么用它

在众多闭环控制方案中,PID 控制器凭借其稳定性与鲁棒性,成为了解决 Pacing 难题的工业标准。其控制输出源自比例(P)、积分(I)、微分(D)三项核心组件的叠加。若定义当前偏差为 e(t) = 目标累计消耗 r(t) − 实际累计消耗 y(t)(其中 e>0 意味着消耗落后,需要正向干预加速):

u(t) = Kp·e(t)  +  Ki·∫e(τ)dτ  +  Kd·de(t)/dt
       └ 比例 P ┘   └── 积分 I ──┘   └── 微分 D ──┘
        看当前偏差      看历史累积        看变化趋势
控制项观测维度在 Pacing 链路中的业务作用调参过激的副作用
P(比例)瞬时偏差幅度消耗落后越严重,系数抬升越激进,旨在实施极速瞬时纠偏极易在目标基准线边缘引发高频震荡
I(积分)历史偏差累积致力于消除稳态误差,解决系统性长期欠投的顽疾累积过载会诱发流量反弹过冲与积分饱和
D(微分)偏差变化趋势敏锐捕捉快速逼近目标的趋势并提前抑制过冲容易放大实时竞价环境中的高频数据噪声

Pacing 的 PID 闭环控制框图,从左到右一条主链:目标曲线 r(t)(按流量分布加权)→ 求和点 Σ(作差得误差 e = r − y)→ PID 控制器(标注 P 快速纠偏、I 消稳态误差、D 抑制过冲)→ 执行 u(t)(出价系数 α / p)→ 经 Bidder 出价、竞得、曝光 → 实际累计消耗 y(t);y(t) 经带箭头的红色虚线反馈回 Σ,标注『回灌反馈:秒级延迟 = dead time』。底部旁注:离散实现每周期算 u 映射 α,抗积分饱和需冻结积分,在线只做 base_bid×α 乘法不占竞价预算 闭环控制回路全景展示了从误差对齐到 PID 调节再到出价落地的流转过程。需要注意的是,真实的扣费账单必须历经异步日志回灌方能生效,这段纯滞后(dead time)是制约系统控制稳定性的核心难点。

为何业界普遍采用经典的 PID,而对模型预测控制(MPC)或强化学习(RL)持谨慎态度?究其原因,PID 具备极高的系统透明度与直观的调参理念,且无需构建复杂的被控对象数学模型。诚然,从出价指令下达到最终账单确认的链路是高度非线性的,但在局部的安全工作阈值内,一套调配得当的 PID 已足够保障大盘稳定。在生产环境中,架构师更看重故障发生时的快速定位与阻断能力。需要强调的是,诸多变现团队在剔除易受高频噪声干扰的微分(D)组件后,仅靠 PI 控制便已取得了优异的投放效果。

4.3 离散实现(工程落地就是这么写的)

竞价引擎通常在离散的时间切片中运转:即每隔一个固定的控制周期 Δt(如 10 至 60 秒)执行一次演算。经典位置式(position form)PID 的核心逻辑如下:

# 严格遵循控制周期触发(以独立 campaign 为粒度)
def pacing_tick(camp, now):
    target = camp.target_spend_ratio(now)   # r(t):提取当前时刻的目标累计消耗占比
    actual = camp.actual_spend_ratio(now)   # y(t):通过异步队列获取的真实累计消耗占比
    e = target - actual                     # 计算偏差:e>0 意味着实际消耗落后于目标

    camp.integral += e * DT
    # ---- 抗积分饱和(Anti-windup)处理将在 4.4 节详述,此处需进行 clamp ----
    d = (e - camp.prev_e) / DT
    camp.prev_e = e

    u = KP * e + KI * camp.integral + KD * d

    # 将控制器输出映射为出价系数 α,并限制在安全区间内
    alpha = clamp(1.0 + u, ALPHA_MIN, ALPHA_MAX)   # 限制在如 [0.1, 3.0] 的界限内
    camp.alpha = alpha
    return alpha

上述代码将误差直接累加进积分项并算出绝对的控制输出 u。业界还存在增量式(velocity form)方案,它仅计算相邻两步之间的瞬时增量并进行累加,对热更新与无缝切流更为友好。但在应对复杂的线上排障时,位置式配合显式的抗积分饱和逻辑具备更好的可观测性,因此更为推荐。

在要求极速响应的在线竞价链路中,出价系统只需执行一次简单的乘法,避免了额外的计算延迟:

final_bid = base_bid × campaign.alpha        # α 系数由后台按周期异步刷新,在线链路只负责读取

这完美呼应了 Bidder 竞价服务架构(总览) 中强调的数据面物理隔离法则:复杂的反馈演算必须部署在近线服务中,在线链路仅需加载已就绪的系数。坚决避免在不足 20ms 的竞价耗时限制内处理任何繁冗的迭代运算。

4.4 调参与三个必须处理的坑

若想让 PID 在复杂的竞价环境中稳定运行,工程团队必须妥善处理以下三大陷阱:

陷阱一:积分饱和(Integral Windup) 当系统遭遇显著的流量低谷时,α 即使触达 ALPHA_MAX 上限也无法扭转消耗落后的局面。此时误差 e 持续维持在正区间,导致积分项 ∫e 不断累积至极大的数值。待到流量回归、实际消耗即将贴合目标时,庞大的积分项会驱使 α 继续保持高位,引发严重的系统过冲与超额透支。解决方案是引入抗积分饱和(Anti-windup)机制:一旦侦测到控制输出已饱和,必须冻结或回退积分累积。

    raw = 1.0 + (KP*e + KI*camp.integral + KD*d)
    alpha = clamp(raw, ALPHA_MIN, ALPHA_MAX)
    if alpha != raw:                 # 侦测到控制输出饱和越界
        camp.integral -= e * DT      # 回退当前周期的无效积分(Clamping Anti-windup)

陷阱二:微分项放大高频噪声 广告的曝光与扣费打点本质上是高度离散且带有随机抖动的数据流。如果直接对误差 e 进行微分操作,极易提取出大量的高频噪声 d。工程上的应对策略通常是直接剔除 D 项退居 PI 控制;或者在执行微分前,先为误差信号串联一个一阶低通滤波模块(如 EMA 算法),以平滑高频扰动。

陷阱三:控制系数缺乏归一化 Kp/Ki/Kd 参数组合实际上与特定 campaign 的预算量级、调控时间粒度及大盘流量分布深度绑定。如果用一组硬编码的固定参数去适配所有 campaign,必然会导致大预算计划调节迟缓,而小预算计划陷入震荡。解决该问题的核心在于推行归一化改造,将绝对金额的偏差转化为相对百分比误差。正因如此,系统才能凭借一套核心参数适配跨业务线的复杂场景。

五、分布式落地:dead time 决定了谁来跑 PID

前文推演的 PID 模型暗藏了一个理想的先决预设:「单机实例独占 campaign 且消耗数据即时可见」。然而,真实的 Bidder 系统横跨多个数据中心,计算单元无状态,且伴随着异步账单回传。当我们将 PID 引擎落地到分布式架构时,主宰系统稳定性的核心变量便是反馈延迟。

分布式 Pacing 落地示意图:中心预算/Pacing 服务持有全局预算与目标曲线并聚合真实消耗,向下用粗箭头把预算分片和 Pacing 系数 α 秒级下发给 Bidder 实例集群(实例 A/B/N,均无状态、关键路径只读只算、本地按 α 出价);实例把曝光/计费日志异步写入 Kafka,Kafka 再把真实消耗聚合回中心服务,这条回灌链路的延迟即控制回路的 dead time;中心服务在 campaign 临近耗尽时向实例集群下发熔断;旁注说明中心跑 PID 以获得全局视角、避免各实例抢跑,实例只执行 α,本地仅保留令牌桶做亚秒级限速,且控制周期必须远大于回灌延迟否则震荡 分布式架构剖析图展示了中心节点聚合全局账单并统一下发 α 系数的流转链路。从真实曝光到数据回灌所经历的延迟(dead time),构成了制约控制闭环稳定性的核心瓶颈。

5.1 dead time(纯滞后)是控制系统的头号死敌

在实际系统中,预算消耗无法做到「所见即所得」。从底层算法竞胜,到远端用户屏幕呈现广告曝光,再到网络节点完成计费打点并写入 Kafka,最后由流计算平台聚合回中枢控制器,整个链路存在秒级甚至分钟级的延迟。这意味着控制器当前获取的 y(t) 仅仅是历史状态的残像。

在控制理论中,这种现象被称为纯滞后(dead time),它是破坏系统稳定性的核心要素。若系统基于过时的测量指标下发干预指令,往往在调节效果显现前就叠加了多轮控制动作,最终导致系统剧烈震荡。应对纯滞后的工程铁律非常明确:控制周期必须远大于反馈延迟。

5.2 谁来跑 PID:中心集权 vs 本地割据

在分布式架构中,PID 引擎的部署位置直接决定了系统的稳定性。

架构模式核心运转机制核心优势潜在风险
中心式(主流)全局账单汇聚于中心节点运行统一 PID,随后广播全局系数 α具备全局视角,避免底层实例相互抢跑;确保大盘系数统一强依赖下发通道的稳定性;网络广播存在延迟
本地式各底层实例基于本地预算分片,独立运行微型 PID摆脱网络下发依赖,本地响应速度快遭遇流量倾斜时各节点调控极易分裂;实例扩缩容易导致状态紊乱

在生产实践中,业界普遍采用中心式 PID 架构。由中心集群聚合全局数据,运行闭环演算并生成统一的 α 系数,随后将其与预算分片周期性地下发至底层实例。底层实例被剥离了复杂调控逻辑,仅需遵循两条原则:在毫秒级的出价链路中严格执行 α 系数;在本地维护高吞吐的令牌桶,用于抵挡突发流量、死守亚秒级限流底线。需要强调的是:

这套设计也扫清了前序篇章的疑云:令牌桶与 PID 并非互斥,而是互补的搭档。PID 负责中长期的平稳调控,而令牌桶则在底层提供瞬时防爆护盾。

5.3 和「不超投」的分片如何默契协同

Pacing 系数 α 负责精细打磨消耗曲线的平滑度;而本地预算分片配合熔断机制,则构筑了防范超投的底线。两者协同运作:在常规投放中,系统通过微调 α 来贴合目标曲线;一旦某个 campaign 逼近预算枯竭的边缘,中心节点会下发最高优先级的熔断指令(将 α 抹零或切断参竞链路)。换言之,Pacing 是常态微调,而熔断是极限阻断。在防范超投的底线面前,曲线的平滑度必须无条件让路。

六、Pacing 不是孤岛:与其它模块的激烈博弈

在需求方平台(DSP)的复杂架构中,Pacing 机制并非孤立存在,它必须与其他核心模块进行深度协同与博弈:

七、可观测性与生产取舍

Pacing 偏差具有隐蔽的长尾累积效应,若缺乏完善的监控,极易在不知不觉中偏离目标。以下是历经生产环境验证的观测规范与架构抉择:


行业黑话总结:Pacing(平滑投放)保障预算时间分布;目标消耗曲线 r(t) 定宽全局节奏;ASAP 模式放任无差别消耗;节流策略依赖投放概率 p 粗放丢弃;出价系数 α 顺应响应曲线精准降价;PID 控制器以闭环纠偏;稳态误差靠积分项消除;积分饱和(windup)极易诱发灾难级超投;纯滞后(dead time)是分布式控制死敌;令牌桶负责亚秒级限流兜底防爆;熔断机制坚守预算枯竭底线。

深入理解了 Pacing 的闭环控制与排障细节后,关于控制回路前置的目标曲线 r(t) 究竟应如何精细化构造,请移步阅读下篇:Pacing 目标曲线:时段消耗权重怎么设计。

延伸阅读


–views
Share this post on:

Previous Post
Pacing 目标曲线:时段消耗权重怎么设计
Next Post
Bidder 频次控制工程篇:多维度限频的存储选型与读写分离