Doris在广告技术中的应用:实时竞价分析系统
关键词:Doris数据库、实时竞价(RTB)、广告技术、实时分析、高并发查询
摘要:在广告技术领域,实时竞价(RTB)系统需要在毫秒级内完成用户画像匹配、竞价策略计算和效果分析。本文将深入探讨Apache Doris如何凭借其“实时写入+高效查询”的特性,成为广告实时竞价分析的核心引擎。我们将从RTB业务场景出发,结合Doris的技术原理、实际案例和优化技巧,为你揭开“广告系统如何在眨眼间完成万亿数据决策”的神秘面纱。
背景介绍
目的和范围
广告行业的“实时性”竞争已进入“毫秒级”时代:当用户打开APP的瞬间,广告系统需要在100ms内完成“用户标签匹配→广告候选池筛选→竞价计算→效果预估→最终排序”全流程。本文聚焦“实时竞价分析”这一关键环节,重点讲解Doris数据库如何解决广告主/平台在RTB场景中的三大核心需求:
- 高并发实时数据写入(如每秒百万级广告请求日志)
- 复杂多维聚合查询(如按“用户地域+广告位+时段”统计点击率)
- 毫秒级响应的在线分析(如实时调整竞价策略)
预期读者
- 广告技术(AdTech)领域的后端开发/数据工程师
- 对实时数据库感兴趣的技术爱好者
- 希望优化广告投放效果的运营/产品经理
文档结构概述
本文将按照“场景需求→技术原理→实战案例→未来趋势”的逻辑展开:首先用“双11大促”的广告投放场景引出RTB分析需求;然后拆解Doris的核心技术(如MPP架构、列式存储、预聚合)如何匹配这些需求;接着通过真实代码案例演示Doris在广告效果分析中的落地;最后探讨Doris与AI、数据湖结合的未来方向。
术语表
核心术语定义
- RTB(Real-Time Bidding)实时竞价:广告交易的“拍卖场”,用户每一次页面刷新都会触发一次实时拍卖,广告主通过竞价获得曝光机会。
- Doris:Apache顶级项目,一款面向实时分析的MPP(大规模并行处理)数据库,支持高并发写入和低延迟复杂查询。
- CTR(Click-Through Rate)点击率:广告曝光后被点击的比例(点击数/曝光数),是衡量广告效果的核心指标。
- CPC(Cost Per Click)单次点击成本:广告主为每次点击支付的费用,直接影响投放ROI(投资回报率)。
缩略词列表
- MPP:Massively Parallel Processing(大规模并行处理)
- OLAP:Online Analytical Processing(在线分析处理)
- Kafka:一种高吞吐量的分布式消息队列,常用于实时数据传输。
核心概念与联系
故事引入:双11的“广告大战”
想象一下双11当晚20:00,用户小明打开某电商APP,首页弹出一个“运动鞋”广告——这个广告的展示背后,藏着一场“看不见的战争”:
- 用户行为触发:小明打开APP的瞬间,APP向广告平台发送请求(包含小明的设备ID、地理位置、历史浏览记录等50+维度数据)。
- 广告候选池筛选:广告平台需要从1000万+广告库中,快速筛选出符合小明兴趣(如“运动鞋”)、广告主预算充足、且未重复曝光的候选广告(最终选出100条)。
- 实时竞价与排序:对100条候选广告,广告平台需要计算每条的“预估CTR”和“竞价价格”,最终选择“ECPM(千次曝光收益)=预估CTR×竞价×1000”最高的广告展示。
- 效果数据回流:广告展示后(曝光),若小明点击,点击数据会实时回传到分析系统,用于优化后续的竞价策略(比如“某地域用户对A品牌运动鞋点击率高,可提高该品牌竞价”)。
整个过程必须在100ms内完成,否则用户会因页面加载慢而流失。而支撑这一切的“大脑”,正是实时竞价分析系统——它需要同时处理“百万级/秒的广告请求数据写入”和“毫秒级的多维聚合查询”,Doris正是这个场景的“最佳拍档”。
核心概念解释(像给小学生讲故事一样)
核心概念一:实时竞价(RTB)的“三兄弟”
RTB系统就像一个“快速拍卖市场”,有三个关键角色:
- 广告请求(Request):用户打开页面时,APP发出的“我需要广告”的信号(类似“我要买水果”的顾客)。
- 竞价响应(Bid):广告主收到请求后,快速决定“我出多少钱买这个曝光”(类似水果摊主喊价“苹果10元/斤”)。
- 效果数据(Impression/Click):广告展示后(曝光),用户是否点击的结果数据(类似“顾客买了苹果还是没买”)。
这三个角色需要“手拉手”工作:广告请求触发竞价,竞价结果决定展示哪个广告,效果数据则用来优化下次竞价——整个过程需要“实时数据+实时分析”的闭环。
核心概念二:Doris数据库的“超能力”
Doris可以比作一个“超级高效的图书馆管理员”,它有三个“超能力”:
- 快速收书(实时写入):不管每天有多少新书(数据)送来(比如每秒100万条广告日志),它都能快速整理上架(写入存储),不排队、不堵车。
- 快速找书(高效查询):当你需要查“20-30岁女性用户,昨晚8点在上海看到的运动鞋广告点击量”时,它能像扫描所有书架一样并行查找,几毫秒就给出结果。
- 自动整理书(预聚合):它会提前把常用的“书组合”(比如“按地域+年龄分组的点击量”)整理好,下次再查同样的组合时,直接拿整理好的结果,不用重新翻所有书。
核心概念三:RTB分析的“三大刚需”
RTB分析就像“给广告效果做体检”,需要满足三个“急需求”:
- 快:广告主需要“现在”就知道“刚投放的广告效果如何”,不能等第二天(类似“刚考完试就想知道分数”)。
- 准:分析结果必须准确,比如“某地区点击率3%”不能算成“5%”,否则竞价策略会乱(类似“买菜称重量不能出错”)。
- 全:需要同时看多个维度(地域、年龄、广告位、时段)的效果,不能只看一个维度(类似“不能只看数学成绩,还要看语文、英语”)。
核心概念之间的关系(用小学生能理解的比喻)
RTB的“三兄弟”(请求、竞价、效果)就像“快递的运输链”:请求是“下单”,竞价是“运输”,效果是“签收”。而Doris就像“快递中转站”,它需要:
- 快速接收“下单数据”(实时写入)→ 对应Doris的实时写入能力。
- 快速查“某个区域、某类商品的运输情况”(多维查询)→ 对应Doris的高效查询能力。
- 提前整理“常查的运输数据”(预聚合)→ 对应Doris的预聚合优化。
简单说:RTB需要“快准全”的分析,Doris正好有“快收书、快找书、自动整理书”的超能力,两者是“天生一对”。
核心概念原理和架构的文本示意图
RTB分析系统架构: 用户行为 → 广告请求(Kafka)→ Doris(实时写入)→ 竞价策略计算(实时查询)→ 广告展示 → 效果数据(Kafka)→ Doris(实时写入)→ 效果分析(离线/实时查询)→ 优化竞价策略Mermaid 流程图
核心算法原理 & 具体操作步骤
Doris能支撑RTB分析的关键,在于其“MPP架构+列式存储+预聚合”三大核心技术。我们逐一拆解:
1. MPP架构:像“多个人一起搬砖”的并行处理
MPP(大规模并行处理)是Doris的“体力担当”。当需要处理一个复杂查询(比如统计全国31个省份的广告点击率),Doris会把查询拆成31个小任务,分别交给31台服务器同时计算(就像31个人同时搬砖),最后把结果汇总。这种“分而治之”的方式,让Doris在处理海量数据时依然快速。
类比生活:你要数清楚一整个操场的人数,一个人数太慢?Doris会把操场分成31个区域,每个区域派一个人统计,最后把31个数字相加——速度快了31倍!
2. 列式存储:像“按科目整理试卷”的高效存储
传统数据库是“行式存储”(按一行一行存储数据),而Doris用“列式存储”(按一列一列存储)。例如广告日志的“地域”“广告ID”“是否点击”三列,列式存储会把所有“地域”值放一起,所有“广告ID”放一起,所有“是否点击”放一起。
为什么更高效?
RTB分析常用“按地域统计点击数”,列式存储只需要读取“地域”和“是否点击”两列数据(其他列如“设备ID”用不到),而行式存储需要读取整行数据(包括用不到的列)。列式存储的“按需读取”大大减少了数据传输量,查询更快。
类比生活:你有一摞考试卷,行式存储是“张三的语文、数学、英语卷”“李四的语文、数学、英语卷”……列式存储是“所有学生的语文卷”“所有学生的数学卷”“所有学生的英语卷”。当你需要统计全班数学平均分,列式存储只需要拿“数学卷”那一摞,行式存储需要翻每一份试卷的数学成绩——显然列式更快!
3. 预聚合:像“提前算好错题本”的智能优化
Doris的“预聚合”功能会自动将常用的聚合结果(如“地域+广告ID的点击数”)提前计算并存储。当用户再次查询同样的聚合条件时,直接读取预聚合的结果,无需重新扫描全量数据。
具体实现:通过创建“物化视图”(Materialized View),Doris会在数据写入时,同时更新预聚合的结果。例如,创建一个按“地域+广告ID”聚合的物化视图,每次写入新的广告日志时,Doris会自动更新该视图中的“点击数”和“曝光数”。
类比生活:你每天需要统计“苹果和香蕉的销量”,预聚合就像提前准备一个“苹果香蕉销量表”,每次卖出苹果或香蕉时,直接在表上加减数字。下次老板问“苹果卖了多少”,直接看表就行,不用翻所有销售小票重新计算。
数学模型和公式 & 详细讲解 & 举例说明
在RTB分析中,核心是计算ECPM(千次曝光收益),它决定了广告的排序和展示优先级。ECPM的计算公式为:
E C P M = 预估 C T R × 竞价价格 × 1000 ECPM = 预估CTR \times 竞价价格 \times 1000ECPM=预估CTR×竞价价格×1000
其中:
- 预估CTR(Click-Through Rate):广告被点击的概率,通过历史数据(如“同用户标签+同广告创意的点击率”)实时计算。
- 竞价价格:广告主为每次点击愿意支付的费用(CPC)。
举例说明
假设广告A的预估CTR是2%(0.02),竞价价格是5元(CPC=5),则:
E C P M A = 0.02 × 5 × 1000 = 100 元 ECPM_A = 0.02 \times 5 \times 1000 = 100元ECPMA=0.02×5×1000=100元
广告B的预估CTR是1.5%(0.015),竞价价格是6元,则:
E C P M B = 0.015 × 6 × 1000 = 90 元 ECPM_B = 0.015 \times 6 \times 1000 = 90元ECPMB=0.015×6×1000=90元
此时广告A的ECPM更高,会被优先展示。
Doris如何支撑ECPM计算?
Doris需要实时提供“预估CTR”的计算依据,即:
预估 C T R = 近期同维度(用户地域 + 年龄 + 广告 I D )的点击数 近期同维度的曝光数 预估CTR = \frac{近期同维度(用户地域+年龄+广告ID)的点击数}{近期同维度的曝光数}预估CTR=近期同维度的曝光数近期同维度(用户地域+年龄+广告ID)的点击数
例如,要计算“25-30岁、上海用户、广告ID=123”的预估CTR,Doris需要快速查询:
C T R = 点击数(地域 = 上海 , 年龄 = 25 − 30 , 广告 I D = 123 ) 曝光数(地域 = 上海 , 年龄 = 25 − 30 , 广告 I D = 123 ) CTR = \frac{点击数(地域=上海, 年龄=25-30, 广告ID=123)}{曝光数(地域=上海, 年龄=25-30, 广告ID=123)}CTR=曝光数(地域=上海,年龄=25−30,广告ID=123)点击数(地域=上海,年龄=25−30,广告ID=123)
通过Doris的列式存储和预聚合,这个查询可以在毫秒级完成——因为“点击数”和“曝光数”已经按“地域+年龄+广告ID”预聚合存储,无需扫描全量数据。
项目实战:代码实际案例和详细解释说明
开发环境搭建
我们以“广告效果实时分析”为例,演示Doris的部署和使用。假设环境如下:
- 操作系统:CentOS 7
- Doris版本:1.2.5(最新稳定版)
- 数据来源:Kafka(实时接收广告曝光/点击日志)
- 工具:DBeaver(数据库管理工具)
步骤1:部署Doris集群
参考官方文档,部署3节点Doris集群(1 FE节点+2 BE节点),确保集群状态健康(通过SHOW PROC '/cluster';查看)。
步骤2:创建Kafka主题
创建两个Kafka主题:ad_impression(曝光日志)和ad_click(点击日志),用于接收实时数据。
源代码详细实现和代码解读
1. 设计Doris表结构(广告曝光表)
广告曝光日志包含以下字段(简化版):
log_time:日志时间(精确到秒)user_id:用户IDregion:用户地域(如“上海”“北京”)age_group:用户年龄分组(如“20-25”“26-30”)ad_id:广告IDis_impression:是否曝光(固定为1,标识曝光事件)
Doris表采用列式存储+预聚合设计,建表语句如下:
CREATETABLEIFNOTEXISTSad_impression(log_timeDATETIMENOTNULLCOMMENT'日志时间',regionVARCHAR(20)NOTNULLCOMMENT'用户地域',age_groupVARCHAR(20)NOTNULLCOMMENT'年龄分组',ad_idINTNOTNULLCOMMENT'广告ID',impression_countBIGINTSUMDEFAULT'0'COMMENT'曝光数(预聚合)')ENGINE=OLAP AGGREGATEKEY(log_time,region,age_group,ad_id)PARTITIONBYRANGE(log_time)(PARTITIONp202401VALUESLESS THAN('2024-02-01'),PARTITIONp202402VALUESLESS THAN('2024-03-01'))DISTRIBUTEDBYHASH(ad_id)BUCKETS16PROPERTIES("replication_num"="3","storage_format"="V2");关键参数解释:
AGGREGATE KEY:定义预聚合的维度(时间、地域、年龄、广告ID),Doris会按这些维度自动聚合impression_count(曝光数)。PARTITION BY RANGE:按时间分区(每月一个分区),便于历史数据管理和查询加速。DISTRIBUTED BY HASH:按广告ID哈希分桶(16个桶),确保数据均匀分布在BE节点,避免热点。
2. 设计广告点击表(类似曝光表)
点击表结构与曝光表类似,增加click_count字段:
CREATETABLEIFNOTEXISTSad_click(log_timeDATETIMENOTNULLCOMMENT'日志时间',regionVARCHAR(20)NOTNULLCOMMENT'用户地域',age_groupVARCHAR(20)NOTNULLCOMMENT'年龄分组',ad_idINTNOTNULLCOMMENT'广告ID',click_countBIGINTSUMDEFAULT'0'COMMENT'点击数(预聚合)')ENGINE=OLAP AGGREGATEKEY(log_time,region,age_group,ad_id)PARTITIONBYRANGE(log_time)(PARTITIONp202401VALUESLESS THAN('2024-02-01'),PARTITIONp202402VALUESLESS THAN('2024-03-01'))DISTRIBUTEDBYHASH(ad_id)BUCKETS16PROPERTIES("replication_num"="3","storage_format"="V2");3. 实时数据写入(通过Kafka+Doris Stream Load)
使用Doris的Stream Load功能,从Kafka实时拉取数据写入Doris。以曝光数据为例,提交Stream Load任务的curl命令:
curl--location-trusted -u user:passwd\-H"label:impression_load_202403"\-H"Content-Type: application/json"\-H"format: json"\-H"timezone: Asia/Shanghai"\-X PUT http://doris-fe:8030/api/test_db/ad_impression/_stream_load\-d @-<<EOF { "log_time": "2024-03-10 20:00:00", "region": "上海", "age_group": "25-30", "ad_id": 123, "impression_count": 1 } EOF关键参数解释:
label:任务标识,确保幂等性(重复提交同一label的任务不会重复写入)。format: json:指定输入数据格式为JSON。timezone:时区设置,避免时间字段解析错误。
4. 实时查询:计算CTR
现在需要查询“2024-03-10 20:00:00,上海,25-30岁用户,广告ID=123”的CTR,SQL如下:
SELECTi.region,i.age_group,i.ad_id,i.impression_count,c.click_count,(c.click_count/i.impression_count)ASctrFROMad_impression iLEFTJOINad_click cONi.log_time=c.log_timeANDi.region=c.regionANDi.age_group=c.age_groupANDi.ad_id=c.ad_idWHEREi.log_time='2024-03-10 20:00:00'ANDi.region='上海'ANDi.age_group='25-30'ANDi.ad_id=123;执行结果示例:
| region | age_group | ad_id | impression_count | click_count | ctr |
|---|---|---|---|---|---|
| 上海 | 25-30 | 123 | 1000 | 20 | 0.02 |
代码解读与分析
- 预聚合的作用:由于
impression_count和click_count已经按“log_time+region+age_group+ad_id”预聚合,查询时无需扫描原始数据,直接读取预聚合结果,响应时间从秒级缩短到毫秒级。 - JOIN优化:Doris对同维度的表JOIN做了优化(如相同的分区和分桶策略),确保JOIN操作在本地节点完成,减少网络传输。
- 实时性保障:通过Stream Load,数据从Kafka到Doris的写入延迟小于1秒,保证了CTR计算的实时性。
实际应用场景
Doris在广告RTB分析中的应用场景远不止CTR计算,以下是几个典型案例:
1. 广告主实时监控投放效果
某运动鞋广告主需要实时查看“当前投放的广告在各城市的点击率”,以便调整竞价策略(如“杭州点击率低,降低竞价;成都点击率高,提高竞价”)。通过Doris的实时查询,广告主可以在数据看板中看到秒级更新的地域CTR分布。
2. 广告平台流量质量分析
广告平台需要分析“哪些流量位(如APP首页/详情页)的广告转化率高”,从而优先向优质流量位分配高价值广告。Doris支持按“流量位+广告主”维度的实时聚合查询,帮助平台快速识别优质流量。
3. 实时反作弊
广告平台需要检测“同一设备在短时间内大量重复曝光”的异常行为(可能是机器刷量)。Doris的高并发写入能力可以实时接收设备日志,结合快速的“设备ID+时间窗口”聚合查询(如“某设备1分钟内曝光100次”),及时标记异常设备。
工具和资源推荐
1. Doris官方工具
- Doris Manager:集群管理工具,支持一键部署、监控和故障排查。
- Doris SQL CLI:命令行客户端,用于执行SQL查询和管理表。
- Doris Loader:离线数据导入工具(如从Hive、MySQL导入历史数据)。
2. 生态集成工具
- Flink:用于实时数据清洗(如过滤无效广告请求),清洗后通过Flink-Doris Connector写入Doris。
- Superset:数据可视化工具,连接Doris后可快速制作广告效果看板。
- Kibana:结合Elasticsearch,用于Doris集群的性能监控(如QPS、延迟)。
3. 学习资源
- 官方文档:Apache Doris Documentation
- 社区论坛:Doris 社区
- 技术博客:关注“Doris开发者说”公众号,获取最新技术实践案例。
未来发展趋势与挑战
趋势1:实时数据湖与Doris的融合
传统数据湖(如Hudi、Iceberg)擅长存储海量非结构化数据,但实时查询能力较弱。未来Doris可能与数据湖深度集成,实现“湖内实时分析”——广告主可以直接分析湖中的原始日志,无需先将数据导入Doris,进一步降低数据处理链路的复杂度。
趋势2:AI与RTB分析的深度结合
Doris将支持更复杂的AI模型推理(如用机器学习模型预测CTR),通过“查询即推理”的方式,在SQL中直接调用模型,实现“实时数据→实时分析→实时决策”的闭环。例如,在计算ECPM时,直接调用CTR预测模型,而非依赖历史统计值。
挑战:超大规模数据下的性能优化
随着广告数据量的爆炸式增长(如每天万亿条日志),Doris需要解决“超大规模集群的一致性”和“极端并发下的查询延迟”问题。未来可能通过“智能负载均衡”和“自适应查询优化”技术,动态调整资源分配,确保高并发下的稳定性能。
总结:学到了什么?
核心概念回顾
- RTB实时竞价:广告的“快速拍卖”,需要毫秒级的数据分析支撑。
- Doris的三大超能力:实时写入(快速收书)、高效查询(快速找书)、预聚合(自动整理书)。
- CTR与ECPM:广告效果的核心指标,Doris通过预聚合和MPP架构实现快速计算。
概念关系回顾
RTB的“快准全”分析需求,与Doris的“实时写入+高效查询+预聚合”特性完美匹配:
- 实时写入满足“快”(数据及时入库)。
- 高效查询满足“准”(复杂多维分析不延迟)。
- 预聚合满足“全”(多维度组合分析秒级响应)。
思考题:动动小脑筋
- 假设你是广告平台的数据工程师,需要分析“不同手机品牌用户对游戏广告的点击率差异”,你会如何设计Doris表的预聚合维度?为什么?
- 如果广告日志中存在“延迟数据”(如点击数据比曝光数据晚5分钟到达),Doris的预聚合功能可能遇到什么问题?如何解决?
- 除了CTR和ECPM,你还能想到哪些广告效果指标需要实时分析?Doris的哪些特性可以支撑这些指标的计算?
附录:常见问题与解答
Q1:Doris与Hive的区别是什么?为什么RTB分析不用Hive?
A:Hive是离线分析引擎(适合T+1报表),数据写入后需要经过MapReduce计算,查询延迟高(分钟级)。Doris是实时分析引擎,支持实时写入和毫秒级查询,更适合RTB的实时性需求。
Q2:Doris如何保证数据可靠性?
A:Doris通过多副本机制(默认3副本)保证数据可靠性。当某台BE节点故障时,系统会自动从其他副本恢复数据,确保业务不中断。
Q3:Doris的预聚合会增加存储成本吗?
A:会,但通过合理设计预聚合维度(只聚合常用查询的维度),可以平衡存储和性能。例如,若90%的查询是“地域+广告ID”维度,只需创建该维度的物化视图,避免全量聚合。
扩展阅读 & 参考资料
- 《Apache Doris实战》—— 林学森(Doris核心开发者)
- 官方文档:Doris Stream Load 指南
- 技术博客:Doris在字节跳动广告场景的实践(示例链接)