Skip to content
Charles Shao
Go back

广告多维报表与漏斗分析实战

views

有了 列存 OLAP实时数仓分层,广告主后台最难的那一块才真正登场:多维报表要快,漏斗/留存要准,UV 还要对得起结算。本篇把「物化与预聚合、漏斗语义、Bitmap/HLL 去重、API 加速」接到 AdTech 日常问法上,作为本系列收官。

本文是实时数仓与 OLAP 系列的第 3 篇(收官 · 报表实战)。 全系列 3 篇:

  1. OLAP(开篇)· 列式存储原理与 ClickHouse/Doris
  2. 实时数仓 · 分层设计与 Lambda/Kappa 架构
  3. 广告多维报表与漏斗分析实战(本篇)

一句话定位:报表体验 = 正确的指标语义 × 可命中的预聚合 × 可承受的去重成本;列存解决「扫得动」,物化与 Bitmap 解决「问得起、算得准」。

TL;DR

Table of contents

Open Table of contents

1. 广告多维报表到底在问什么

一张广告主看板,表面是表格,底下是固定模板的分析问法:

指标:花费、曝光、点击、CTR、CVR、UV、平均 eCPM …
维度:日期、计划、创意、媒体、地域、操作系统、流量类型 …
过滤:广告主、时区、结算币种、去无效点击 …

组合数是指数级的。若每次请求都对亿级明细 GROUP BY,再强的列存也会在并发下抖动。策略只有两条:

  1. 认出高频问法(往往就一二十种维度组合),为它们建预聚合/物化;
  2. 长尾问法限流、降级到异步导出或抽样。

2. 预聚合与物化视图:热路径优先

多维报表加速路径。冷路径:明细事实表亿级行 → 即时 GROUP BY 任意维度 → 秒到分钟级吃满集群。热路径:Flink 写入明细到 OLAP → 物化视图/Rollup(campaign×media×hour)→ ADS 专题表(漏斗/留存/UV Bitmap)→ 报表 API P99 < 1s。底部说明用固定问法的稳定延迟换灵活度,把高频组合物化。

生产报表默认走热路径;冷路径留给数据同学的临时 dig。

2.1 预聚合表(人工 Rollup)

例如日级:

-- 示意:按计划 × 媒体 × 天
SELECT
  event_date, campaign_id, media_id,
  sum(cost) AS cost,
  count() AS imps,
  countIf(is_click) AS clks
FROM dwd_impression
GROUP BY event_date, campaign_id, media_id;

Flink 窗口产出,或 OLAP 定时刷新。优点是可控;缺点是维度组合一多,表数量与作业数量爆炸。

2.2 物化视图 / Projection

ClickHouse 物化视图Projection,Doris 同步/异步物化视图,把「常见 GROUP BY」交给引擎增量维护。报表 SQL 仍写明细表,优化器改写到 MV——产品与工程接口更干净。

设计要点:

2.3 ROLLUP / CUBE:小心组合爆炸

SQL 里的 CUBE / ROLLUP 可以一次算出多个维度小计,很好写,也很容易把中间结果撑爆:

SELECT campaign_id, media_id, os, sum(cost)
FROM dwd_impression
WHERE event_date = today()
GROUP BY CUBE (campaign_id, media_id, os);

三维 CUBE 产生 8 种组合;维度再加,存储与写入线性到指数恶化。实务建议:

3. 漏斗分析:有序递减,而不是四个独立计数

广告漏斗:曝光 UV 100万 / PV 120万 → 点击 UV 4.5万、CTR≈3.8% → 落地页 UV 2.8万、到达率≈62% → 转化 UV 3200、CVR≈11.4%。每步是严格递减的 UV,精确去重用 Bitmap,计费侧慎用近似。

漏斗每一步都是「穿过了上一步的用户集合」再求基数。

3.1 语义要件

  1. 步骤有序:同一用户先曝光、再点击、再落地、再转化(或归因窗口内可连上);
  2. 窗口:会话漏斗、当日漏斗、7 日归因窗——产品必须选一种写进口径文档;
  3. 主体:通常按 user_id / device_id;注意登录态与设备图混淆;
  4. 去重粒度:步骤内 UV 要去重,步骤间靠集合运算。

错误示范:用「点击 PV / 曝光 PV」当 CTR,又在漏斗第二步用 UV——上下游不可比,广告主一定会来对线。

3.2 工程实现素描

转化与点击之间若走广告归因(点击归因 / 末次曝光),漏斗步骤边界要以归因结果表为准,而不是原始转化日志时刻——否则和结算对不上。

3.3 漏斗的常见歪楼

歪楼后果纠正
用 PV 当漏斗步骤CTR 虚高、同用户膨胀步骤一律 UV
窗口与结算归因窗不一致看板 CVR ≠ 账单对齐归因文档
步骤时间戳用 processing-time乱序下漏算event-time + 允许迟到
跨设备未打通却宣称「用户漏斗」同人多次计明确主体是设备还是人
把「可见曝光」与「竞价成功」混为一步优化方向跑偏拆开定义

漏斗是产品语义,不是 SQL 语法糖——先写进口径中心,再写查询。

4. 精确去重 vs 近似:Bitmap 与 HLL

去重两条路。左:Roaring Bitmap——用户 ID 字典编码 → 按日/维度挂 Bitmap → 交并差支持漏斗留存 → 可复现可对账,适合计费结算 UV。右:HLL——固定小 sketch、可 MERGE、误差约 1–2%、无法还原集合,适合量级趋势不宜结算。

交集问法(漏斗、留存)几乎点名 Bitmap;HLL 擅长「大概多少」。

4.1 Bitmap(Roaring)

ClickHouse AggregateFunction(groupBitmap, …) / Bitmap* 函数族、Doris Bitmap 类型,都是广告 UV 的常用武器。

4.2 HLL

规则:钱相关、合同相关 → Bitmap/精确;体感相关 → HLL 可。 含糊就会在客诉时付出十倍成本。

4.3 字典爆炸与基数治理

Bitmap 前面的「用户 ID → 整数」字典本身可能成为瓶颈:全站设备 ID 几十亿时,全局字典不可持。常见拆法:

没有基数治理的 Bitmap,会在「终于做对 UV」的第二天把磁盘写满。

5. 留存:cohort 的集合运算

N 日留存本质是:

在 D0 进入队列的用户集合,有多少出现在 D0+N 的活跃集合里。

落地:

  1. D0 转化用户 Bitmap(或首曝用户);
  2. 之后每日活跃 Bitmap(有曝光/打开/回流);
  3. retain = |B0 ∩ BN| / |B0|

预聚合时按 cohort_date × N × 维度 存分子分母,看板只读 ADS。不要每次从明细重放 30 天事件流——除非你在做口径研究。

6. 报表 API 加速清单

Serving 不只是「查 OLAP」:

手段作用注意
物化 / Rollup降扫描量覆盖高频组合
分区 + 排序键裁剪与局部性与过滤条件对齐
API 结果缓存挡重复看板刷新键含口径版本与时区
布隆 / 空缓存挡乱 ID 穿透布隆过滤器
查询隔离看板与 dig 分队列避免 explorative SQL 打死集群
限流与异步导出兜底长尾大查询超限改任务系统

缓存层要带口径版本号:发版切换 MV 时旧缓存必须失效,否则广告主看到「两套数轮播」。

6.1 接口契约建议

报表 API 对外至少固定这些字段:

前端展示「数据更新至 xx:xx」比盲目 15 秒轮询有价值得多——也减少对集群的无效击打。

7.1 从竞价到报表的指标闭链

一条曝光从出价到进看板,途经:竞价日志 → 曝光上报 →(可选)可见性校准 → 计费 → DWD → ADS。闭链检查问三个问题:

  1. 水量:竞价成功数与曝光数比例是否在合理带?
  2. 金额:计费花费 ∑ 是否等于结算域?
  3. :看板 UV 是否与去重规则下的计费侧一致?

任何一环用「另一套近似」偷懒,都会在广告主对账单时被揪出来。布隆过滤器适合挡不存在的 ID击穿缓存(见 布隆),但不能充当 UV 真相。

参考


views
Share this post on:

Previous Post
分布式共识(开篇)· Paxos 与 Raft 图解
Next Post
特征编码:把文字菜谱翻译成秤上的克数