第二章 广告系统架构总览
大家好,我是老张。今天咱们聊聊广告系统的整体架构。说实话,我见过不少团队一上来就埋头写代码,结果做到一半发现数据流不通、资金流对不上,最后推倒重来。嗯,这章就是帮你避免这种悲剧的。
2.1 广告系统整体架构图
先看一张宏观图。广告系统说白了就是三个圈:业务层、引擎层、数据层。我习惯把这三层比作「大脑、心脏、血管」——业务层做决策,引擎层做计算,数据层做流转。
核心架构分层:
- 业务层:广告主操作界面、代理商管理、审核系统
- 引擎层:投放引擎、检索服务、排序服务、计费服务
- 数据层:实时数据管道、离线数仓、报表系统
你想想看,一个用户打开APP看到广告,背后要经过多少环节?从广告主创建计划,到系统检索候选广告,再到排序选出最优,最后计费扣钱——整个过程必须在几百毫秒内完成。我曾经在项目中遇到过,因为架构分层不清晰,导致一个计费bug影响了三天数据,最后对账对到崩溃。
2.2 核心子系统详解
2.2.1 投放系统
投放系统是广告主的「控制台」。我个人习惯把它拆成三个模块:
- 计划管理:创建广告计划、设置预算、定向条件
- 创意管理:上传素材、配置落地页、A/B测试
- 审核模块:自动审核+人工审核,合规检查
这里有个坑。我曾经见过一个团队,把审核逻辑直接写在投放系统的业务代码里。结果审核规则一变,整个投放系统都要重新部署。我建议把审核做成独立的微服务,通过RPC调用。这样审核规则更新时,投放系统完全不受影响。
2.2.2 检索系统
检索系统是广告引擎的「入口」。用户发起请求时,系统要在毫秒级内从百万级广告中找出符合条件的候选集。
我的经验:检索系统最怕的就是「全量扫描」。一定要建立倒排索引,按地域、人群、时段等维度建立索引分片。我曾经优化过一个检索服务,把索引从MySQL迁移到Elasticsearch,查询耗时从800ms降到了50ms。
检索的核心流程:
- 接收用户请求(设备ID、地理位置、上下文信息)
- 解析定向条件(性别、年龄、兴趣标签)
- 从索引中召回候选广告(通常取Top 200-500)
- 返回候选列表给排序系统
2.2.3 排序系统
排序系统是广告引擎的「大脑」。它决定哪个广告最终展示给用户。说白了,就是算分——谁的分高谁上。
排序一般分两阶段:
- 粗排:用轻量模型快速过滤,把候选集从500降到50
- 精排:用深度学习模型精确计算CTR、CVR,选出最优
注意:排序系统的延迟要求极高。精排模型如果超过50ms,整个广告请求就会超时。我建议把模型推理做成异步+缓存,避免每次请求都重新计算特征。
2.2.4 计费系统
计费系统是广告系统的「钱袋子」。它负责计算每次点击或展示的费用,并实时扣减广告主预算。
常见的计费模式:
| 计费模式 | 说明 | 适用场景 |
|---|---|---|
| CPM | 按千次展示付费 | 品牌广告 |
| CPC | 按点击付费 | 效果广告 |
| CPA | 按转化付费 | 应用下载、表单提交 |
| oCPM | 按目标转化出价 | 智能优化场景 |
计费系统最核心的是「防超跑」和「防重复扣费」。我曾经在项目中遇到过,因为并发扣费没做好,同一个点击被扣了两次钱。广告主投诉到CEO那里,那叫一个惨。后来我们引入了Redis分布式锁+消息队列,确保每条计费记录只处理一次。
2.2.5 报表系统
报表系统是广告系统的「仪表盘」。广告主和运营人员都靠它看数据、做决策。
报表系统要处理的数据量很大——每天几亿条曝光、点击、转化记录。我建议采用Lambda架构:
- 实时层:用Flink处理秒级数据,展示实时消耗、实时点击
- 离线层:用Spark处理小时级/天级数据,生成详细报表
- 服务层:用ClickHouse或Doris提供OLAP查询
关键指标:
- 展示量、点击量、转化量
- 消耗金额、平均CPC、平均CPM
- CTR(点击率)、CVR(转化率)、ROI(投资回报率)
2.3 数据流与资金流
广告系统有两条核心流:数据流和资金流。这两条流必须严格分离,但又需要最终对账。
2.3.1 数据流
数据流是「信息」的流动。从用户请求到广告展示,再到点击和转化,每一步都会产生事件数据。
典型的数据流路径:
- 用户发起请求 → 检索系统收到请求ID
- 检索系统返回候选广告 → 排序系统计算分数
- 排序系统返回最终广告 → 展示给用户
- 用户点击广告 → 计费系统记录点击事件
- 用户完成转化 → 报表系统记录转化事件
这里要注意,数据流必须保证「不丢不重」。我建议用Kafka作为消息中间件,每条消息都带上唯一ID,下游做幂等处理。
2.3.2 资金流
资金流是「钱」的流动。广告主充值、消耗、退款,每一步都涉及真金白银。
资金流的核心环节:
- 充值:广告主通过支付网关充值,资金进入广告平台账户
- 消耗:每次点击或展示,从广告主账户扣减对应金额
- 退款:广告计划暂停或余额不足时,剩余资金退回
- 结算:平台与媒体方结算,支付流量费用
避坑指南:我曾经因为资金流和数据流混在一起,导致对账时差了十几万。后来我强制要求:数据流走Kafka,资金流走数据库事务+消息队列。两条流各自独立,每天凌晨做一次对账,发现差异立即告警。
2.4 架构设计要点总结
嗯,说了这么多,最后总结几个关键点:
- 分层清晰:业务层、引擎层、数据层各司其职,不要混在一起
- 高可用:每个子系统都要做冗余和降级,单点故障不能影响全局
- 可扩展:广告量增长时,系统能通过加机器水平扩展
- 可对账:数据流和资金流必须能对得上,这是底线
下一章我会深入讲投放系统的设计细节,包括定向策略、预算控制、频控算法等。到时候咱们再细聊。
个人建议:如果你正在设计广告系统,先从「最小闭环」开始——先让一个广告能正常展示、点击、计费、出报表。然后再逐步加功能。别一开始就想做完美系统,那只会让你陷入无尽的细节中。
好了,这章就到这里。有问题欢迎在评论区交流,咱们下章见。