系统设计:广告投放系统

从零设计一个广告投放系统,覆盖广告检索与召回、定向与用户画像、竞价与预算控制(pacing)、频次控制、计费与归因、反作弊与效果衡量,包含容量估算、架构图、数据模型、选型对比与面试追问。

系统设计:广告投放系统

广告系统是「毫秒级竞价 + 海量检索 + 精准定向 + 严格预算」的复合体:一次广告请求要在 100ms 内从千万级广告中选出最合适的一条,且不能超预算、不能超频次、不能作弊。它是面试中考察综合能力的高频题。

1. 需求分析

功能性需求

  • 广告投放:广告主创建计划、设置定向、预算、出价
  • 广告检索:根据用户与上下文召回候选广告
  • 排序与竞价:按 eCPM 排序并确定计费价格
  • 计费:按曝光/点击计费,扣费准确
  • 频次控制:同一用户看到同一广告的次数上限
  • 报表:曝光、点击、转化、花费的统计

非功能性需求

  • 响应延迟:P99 低于 100ms(含竞价)
  • 吞吐:峰值 100 万 QPS 广告请求
  • 扣费准确性:不超扣、不漏扣,可对账
  • 可用性:99.99%,广告失败可降级为无广告
  • 预算准确性:日预算不可超投,偏差小于 1%

核心难点

  1. 超低延迟:检索 + 排序 + 竞价全流程必须在百毫秒内
  2. 预算刚性:超投会被广告主投诉,少投则浪费
  3. 归因延迟:转化可能在点击后数天才发生,扣费与统计需异步补偿

2. 容量估算

  • 日广告请求:200 亿次
  • 平均 QPS:200 亿 / 86400 ≈ 23 万
  • 峰值按 4 倍计:约 100 万 QPS
  • 在投广告数:1000 万条,定向维度多样
  • 单次请求召回候选:数千条 → 需粗排降到数百 → 精排几十
  • 曝光日志:日曝光 100 亿条 × 200 字节 ≈ 20 TB/天

结论:检索必须分层(召回 → 粗排 → 精排),否则 1000 万候选不可能在毫秒内算完;日志量巨大,需列式存储 + 流式聚合。

3. 整体架构

用户请求(内容流/搜索)
      │
      ▼
   广告网关(解析请求、取用户画像)
      │
   ┌──┴───────────────┐
   ▼                  ▼
定向检索(倒排索引)   预算/频控校验(内存)
   │                  │
   └────────┬─────────┘
            ▼
        粗排(轻量模型,千→百)
            ▼
        精排(pCTR 模型,百→十)
            ▼
        竞价与分配(定价、扣费)
            ▼
        返回广告 + 曝光监测链接
            ▼
   曝光/点击/转化日志 ──► 流式聚合 ──► 报表 + 预算回写

请求路径

网关解析请求并拉取用户画像,检索层用倒排索引按定向条件召回,预算与频控在内存中快速校验,粗排精排后进入竞价,最终返回广告并附监测链接。

反馈路径

曝光、点击、转化日志经流式管道聚合,实时回写预算消耗与频控计数,并生成报表。

4. 数据模型

广告计划表

ad_campaign (
    campaign_id  BIGINT PRIMARY KEY,
    advertiser_id BIGINT,
    creative_id  BIGINT,
    bid_type     TINYINT,        -- CPM/CPC/CPA
    bid_price    INT,            -- 出价(分)
    daily_budget INT,            -- 日预算(分)
    targeting    JSON,           -- 定向条件
    freq_cap     INT,            -- 频次上限
    status       TINYINT,        -- 投放/暂停/结束
    start_ts     BIGINT,
    end_ts       BIGINT
)

定向倒排索引

index:gender:male      -> [campaign_id ...]
index:city:310000      -> [campaign_id ...]
index:interest:sports  -> [campaign_id ...]

内存中的预算与频控

budget:{campaign_id}     ->  今日已消耗(分),定时持久化
freq:{user_id}:{campaign_id} -> 已曝光次数,TTL 24h

设计要点

  • 定向条件用倒排索引加速,多条件求交集
  • 预算与频控放内存(Redis/本地),避免每次查库
  • 消耗数据定时落盘并对账,防止内存丢失导致超投

5. 广告检索与召回

分层检索

阶段输入输出手段
召回千万级数千倒排索引 + 布尔求交
粗排数千数百轻量特征 + 线性/浅层模型
精排数百数十深度 pCTR 模型
竞价数十1 到 3eCPM 排序 + 定价

召回优化

  • 定向条件构建多路倒排,按「区分度」排序求交(先交小集合)
  • 对热门定向(如全国 + 全年龄)预计算候选集
  • 用布隆过滤器快速排除明显不匹配的广告

粗排的必要性

精排模型重、耗时高,无法对数千候选逐个打分。粗排用极轻的特征(如广告质量分、历史 CTR)快速裁剪,保证精排只处理最有希望的候选。召回与排序的分层设计可参考 推荐系统设计,高并发下的频次与预算校验则与 高并发限流器设计 同源。

6. 定向与用户画像

定向维度

  • 人口属性:性别、年龄、地域
  • 兴趣标签:长期兴趣 + 短期行为
  • 设备与环境:机型、网络、时段
  • 人群包:广告主上传的定向人群(如已购用户)

用户画像构建

  • 离线:基于历史行为用批处理生成长期兴趣标签
  • 近线:小时级更新短期行为标签
  • 实时:请求时结合当前上下文(如刚搜索的词)

画像存储

画像特征宽表存 KV(如 Redis/HBase),按 user_id 读取;兴趣标签存倒排便于反向查询。注意高基数(user_id)不能进倒排索引做广告召回,只能作为过滤。

隐私与合规

  • 画像需脱敏、加密,遵循个人信息保护要求
  • 支持用户关闭个性化推荐(降级为上下文定向)
  • 定向人群包不得用于识别个人身份

7. 竞价与预算控制

eCPM 排序

不同计费方式统一折算为 eCPM 排序:

eCPM(CPM 广告)  = bid_price
eCPM(CPC 广告)  = bid_price × pCTR × 1000
eCPM(CPA 广告)  = bid_price × pCTR × pCVR × 1000

定价方式

  • 一价:按出价结算,简单但易诱导虚高出价
  • 二价(广义第二价格 GSP):按第二名出价 + 最小加价结算,鼓励真实出价

预算控制 Pacing

预算控制解决「预算在一天内平滑消耗」的问题:

策略做法问题
匀速预算均摊到全天流量波动时浪费
快投尽快花完早间耗尽,晚间无曝光
概率节流按剩余预算/剩余时间动态决定是否参竞兼顾平滑与机会
def should_bid(campaign, now):
    elapsed = now - day_start(campaign)
    ideal_spend = campaign.daily_budget * elapsed / DAY_SECONDS
    actual = get_spend(campaign)
    # 超速则降低参竞概率,欠速则提高
    ratio = actual / ideal_spend if ideal_spend else 1
    return random.random() < clamp(1.0 / max(ratio, 0.1), 0, 1)

超投防护

  • 内存扣费 + 定时对账落库,双重校验
  • 预算耗尽立即从召回索引摘除该广告
  • 最终以数据库扣费为准,内存偏差在秒级内校正

8. 频次控制与计费归因

频次控制

  • 按「用户 × 广告」计数,超过上限不再投放
  • 存储用 Redis 计数 + TTL(如 24 小时窗口)
  • 支持多级频控:单广告、单计划、单广告主维度

计费流程

  1. 广告返回时记录「待计费」事件
  2. 收到曝光回执(客户端上报)才真正扣费
  3. 扣费写内存并异步落库
  4. 定时对账,修正内存与数据库偏差

归因模型

模型规则适用
末次点击归因给最后一次点击效果广告主流
首次点击归因给第一次点击品牌曝光
线性平均分配给所有触点多触点分析
时间衰减越近权重越大短周期转化

归因延迟处理

转化可能延迟数天上报,因此:

  • 计费与统计分离,扣费按点击即计(CPC),转化统计异步补齐
  • 支持回溯窗口(如点击后 7 天内转化均计入)
  • 报表区分「实时数」与「结算数」,避免口径混乱

9. 反作弊与效果衡量

常见作弊类型

  • 曝光作弊:刷量机器伪造曝光
  • 点击作弊:诱导点击、点击农场
  • 转化作弊:虚假注册/下单骗取 CPA 结算

反作弊手段

  • 设备指纹与 IP 聚类,识别同一设备的高频异常
  • 行为序列分析,机器点击的间隔与轨迹过于规律
  • 点击率异常检测:远高于同类广告的 CTR 触发人工审核
  • 事后回溯:结算前批量复核可疑流量并扣减

效果衡量指标

指标含义用途
CTR点击率广告吸引力
CVR转化率落地页效果
eCPM千次曝光收益平台收入
ROI/ROAS投入产出比广告主价值
填充率有广告返回的比例流量变现效率

效果实验

  • 用 A/B 实验对比不同出价/排序策略
  • 用增量实验(Geo 实验、PSA)衡量广告真实增量,排除自然转化
  • 关注长期价值,避免只优化短期 CTR 损害用户体验

10. 面试常见问题

Q: 千万级广告如何在 100ms 内检索完?
分层检索:倒排召回(千万→千)、粗排(千→百)、精排(百→十)。每一层都用轻量手段快速裁剪,只有最后一层才上重模型。

Q: 怎么保证不超预算?
内存扣费 + 概率节流(pacing)+ 预算耗尽立即摘除索引 + 定时对账。核心是让消耗速度与预算/时间匹配,并在多个环节做校验。

Q: 一价和二价的区别?
一价按出价结算,广告主倾向于压低出价;二价按第二名加价结算,鼓励按真实价值出价,是主流选择。

Q: pCTR 模型怎么训练?
用历史曝光点击日志做样本,特征含用户画像、广告特征、上下文,标签为是否点击;注意样本偏差(只对已曝光样本有标签)与实时性(需在线更新)。

Q: 频次控制放在哪一层?
放在召回后的校验层,用内存计数快速判断;也可在召回前用布隆过滤器粗筛,减少无效计算。

Q: 转化延迟怎么处理?
计费与统计分离,扣费即时、转化异步回填;设定归因窗口,报表区分实时与结算口径,并对超窗转化单独处理。

Q: 如何衡量广告的真实效果?
A/B 实验 + 增量实验(PSA/Geo 对照)。单纯对比「看过广告 vs 没看过」会有选择偏差,增量实验才是因果意义上的效果。

Q: 反作弊和广告主利益冲突吗?
不冲突。作弊流量损害的是广告主 ROI 与平台长期信任,反作弊是保护生态;结算前复核与扣减是行业通行做法。

总结

广告投放系统的答题主线是检索分层 + 预算刚性 + 归因延迟三件事:检索用召回/粗排/精排三级流水线把千万级候选压到个位数;预算用 pacing 概率节流 + 内存扣费 + 对账三重保证不超投;归因用实时与结算分离应对转化延迟。最后补上定向画像的隐私边界、反作弊与增量实验,就能覆盖这道题的全部考察点。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页