两个团队报的日活差了三成。一方说 42 万,一方说 31 万,会上从"谁的数据有问题"吵到"谁在美化管理层预期",两小时过去两个数都没变。散会后各自回去查:两边 SQL 都没写错,库里也没丢数,差的只是四个默认值——按登录事件还是任意行为事件去重、自然日还是 UTC 切日、含不含测试账号。
这类争执很有规律:默认假设是"对方算错了",而事实是"对方用了另一套默认值"。把默认值摆上桌,多数争议很快收敛,剩下的才真正涉及结论。
1. 口径分歧的四个层次
一个指标从原始事件走到报出的数字,要过四道闸口。每道闸口都有一组"不写下来就各自填"的默认值。
| 层次 | 争论的内容 | 常见默认值(各团队不同) | 差异量级 |
|---|---|---|---|
| 取数范围 | 算谁的 | 全部账号 / 排除测试与内部 / 排除已注销 | 大,常见两位数百分比差 |
| 时间边界 | 算哪一段 | 自然日 / 滚动 24 小时 / 事件时间的时区 | 中,边界附近 1–3% |
| 去重规则 | 一人算几次 | 按 user_id / 按设备 ID / 按会话 | 中大,跨端用户是主要来源 |
| 异常剔除 | 剔除什么 | 刷单、批量脚本、爬虫、补数、回灌 | 视业务,恶意流量大时可达数成 |
前两个层次最容易吵也最容易解决,因为它们能直接写成 SQL 条件;后两个难,涉及"什么算一个人""什么算一次真实行为",而定义问题没有唯一解。经验判据:差异超过 10% 先怀疑取数范围与去重规则,差异在 1–3% 先看时间边界与提交延迟。
2. 分歧不是算错:默认值、伪问题与真问题
把"两边数不一样"等同于"有一边错了",是这类会议最贵的假设。成因大致分布:
| 成因 | 频率 | 表现 | 处理方式 |
|---|---|---|---|
| 默认值不同 | 最常见 | 差异稳定、可重复、比率接近常数 | 对齐默认值,不改代码 |
| 实现 bug | 较少 | 差异随机跳变、只影响某个维度 | 查实现,属工程问题 |
| 数据缺失/延迟 | 常见于实时口径 | 差异随一天中的时刻变化 | 区分"最终值"与"当前值" |
| 上游数据源不同 | 常见 | 差异在特定人群上放大 | 确认事实源归属 |
判断顺序固定为:先问默认值,再看时间点,最后查实现。反过来做,两边会各花两天翻 SQL,最后发现谁都没写错。定位可以脚本化:
-- 口径 A:任意行为事件去重(含测试租户)
SELECT COUNT(DISTINCT user_id) FROM dwd_user_event_di
WHERE dt = '2026-03-01';
-- 口径 B:仅登录事件去重(已排除测试租户)
SELECT COUNT(DISTINCT user_id) FROM dwd_login_di
WHERE dt = '2026-03-01' AND tenant_type <> 'internal';
-- 逐层加条件对齐,观察差额在哪一步被吃掉
反向陷阱:差异稳定不等于口径不同。比值长期是 1.30,也可能是某一边采集始终漏掉某个客户端版本——那是稳定的实现缺陷,不是口径选择。
"哪个口径对"是伪问题:同一个指标在回答不同问题时,本来就该有不同的口径。
| 这个数要回答什么 | 合理口径 | 常被混用的口径 |
|---|---|---|
| 服务容量够不够 | 峰值时段在线会话数 | 日活(平滑掉了峰值) |
| 产品是否被持续使用 | 周内多次活跃用户 | 日活(含一次性回访) |
| 收入对账 | 财务确认口径的付费用户 | 埋点口径的支付点击 |
真问题是"这个数要回答什么,因此必须包含什么、排除什么"。说清楚之后口径是推导出来的,不需要投票。同一个名字被两个问题共用,是口径之争的结构性根源:"活跃用户"在产品、运营、财务、基础设施四个语境下是四个集合,却都写成 dau。
3. 合格口径的四项与变更留痕
口径不是一句话,是一个四元组。缺任何一项,它都会在某个时间点被重新解读。
metric: dau
version: 3
range: ─── 取数范围:谁在里面
entity: 登录态用户
include: [web, ios, android]
exclude: [internal_tenant, qa_seed, deleted_account]
fact_source: dwd_user_event_di ─── 唯一事实源,禁止多源并行
time: ─── 时间边界:算哪一段
window: natural_day
timezone: Asia/Shanghai
event_time: client_ts
dedupe: ─── 去重规则:一人算几次
key: user_id
cross_device: true
anonymous: excluded
filter: ─── 异常剔除:什么不算
bot_rule: rules/bot_v7.yaml
backfill_policy: restate_7d
| 缺项 | 会被怎么误读 | 典型后果 |
|---|---|---|
| 缺范围 | 有人用全量、有人用清洗后 | 长期差 5–30% 且无人知道为什么 |
| 缺时间 | 有人按 UTC、有人按本地 | 跨日指标在月初月末对不上 |
| 缺去重 | 一人多端被算多次 | 移动端扩张期指标虚高 |
| 缺剔除 | 刷量与爬虫进入统计 | 指标被污染后无法回溯清洗 |
口径必然要改:业务变了、采集变了、作弊手段变了。问题在于改完之后历史曲线还能不能连起来。
| 处理方式 | 做法 | 代价 | 适用 |
|---|---|---|---|
| 重算历史 | 用新口径回刷全量 | 成本高、需要旧数据仍在 | 变更小、数据保留完整 |
| 双轨并行 | 新旧口径同时产出 N 期 | 一段时间内两套数并存 | 变更大、需要可比较 |
| 断点标注 | 曲线上打标,不做重算 | 历史不再可比 | 旧数据已不可回刷 |
| 硬切不标注 | 直接换 | 曲线出现台阶 | 任何情况下都不推荐 |
硬切在曲线上留下台阶,三个月后没人记得原因,于是被当成"那次改版提升了 20%"写进复盘。所以变更必须落一条可查记录:
INSERT INTO metric_definition_change
(metric, version, effective_date, change_type, reason, owner, restated)
VALUES
('dau', 3, '2026-03-01', 'dedupe_key',
'user_id 打通多端,改用账号去重', 'data-platform', false);
-- 回看某区间实际生效的版本,用于解释曲线台阶
SELECT date, version FROM metric_definition_version_range
WHERE metric = 'dau' AND date BETWEEN '2026-01-01' AND '2026-06-30';
4. 收口、裁定与字典的失效
同一指标在多系统各有一套实现,是口径分歧的第二种形态:不在会上出现,而在看板里并排显示。收口顺序:定义唯一 → 实现唯一 → 展示可复用,分别靠口径字典、事实表层、指标服务,而不是每个看板自己写 SQL。
| 收口方式 | 强度 | 成本 | 失效方式 |
|---|---|---|---|
| 口径字典(文档) | 弱 | 低 | 无人读、无人改、与实现脱节 |
| 统一事实表层 | 中强 | 中 | 表结构僵化,新问题绕开它 |
| 指标服务/语义层 | 强 | 高 | 成为瓶颈,例外需求外溢 |
| 财务口径裁定 | 最强 | 低 | 只覆盖进入钱的那部分指标 |
"以财务口径为准"有效,是因为它依赖后果的唯一性而非技术权威——对账不平要有人担责。裁定有效的前提是存在承担后果的角色;研发内部指标没有这种角色,照搬只会把争执换个人。
口径字典的成本随条目数非线性上升,主要不是要写,而是读的人必须在决策前想起来读。它最常见的失效不是无人维护,而是字典定义与看板实现长期不一致且无人察觉——直到争议再次爆发。低成本做法是让字典从代码生成:
$ make metric-dict
metric-defs/*.yaml → site/docs/metrics/*.md
check: 每条必须有 owner / fact_source / dedupe.key
$ grep -L 'fact_source' metric-defs/*.yaml ─── 缺项直接失败
争议解决后留一份可引用的裁定记录,否则半年后同样的问题会再吵一次:
dispute: dau_definition
date: 2026-03-08
participants: [data-platform, growth, finance]
question: 日活按登录事件还是任意行为事件
decision: 采用任意行为事件去重口径,v3 生效
scope: 内部看板与增长分析;对外披露仍用财务口径
cost: 历史曲线在 2026-03-01 出现断点,不回刷
owner: data-platform
5. 排错:五种口径争议的现场
| 现象 | 最可能的层次 | 第一步动作 |
|---|---|---|
| 两个数长期差固定比例 | 取数范围 | 拉出两边的排除条件逐条比对 |
| 差异随时间波动、月初最大 | 时间边界 | 确认时区与提交延迟,对齐"最终值" |
| 差异只在移动端出现 | 去重规则 | 检查是否按设备 ID 去重 |
| 指标在促销期间异常高 | 异常剔除 | 检查刷单与批量脚本规则是否生效 |
| 曲线在某日出现台阶 | 口径变更 | 查断点表,确认是否硬切未标注 |
排错最该避免"先改个条件跑一遍看能否对上"。这种试错产出第三个数,把争议从两方变三方。
6. 边界与度量
口径管理本身也要被度量,否则它会膨胀成一份没人用的文档集。
| 度量 | 含义 | 观测方式 |
|---|---|---|
| 口径条目数 | 字典规模 | 与看板指标数比,比值过高说明过度细化 |
| 断点次数 | 一段时间内的口径变更次数 | 半年超过若干次说明上游不稳 |
| 口径争议数 | 单位时间内发生的对比争执 | 会议记录/工单计数 |
| 跨系统不一致率 | 同名指标在不同系统不同值的比例 | 定期抽样比对 |
| 边界场景 | 结论 |
|---|---|
| 一次性分析 | 口径写进分析脚本注释即可,不必入字典 |
| 单人使用的临时指标 | 不需要正式口径,但不要对外引用 |
| 指标不进入任何决策 | 口径管理的收益接近零 |
| 指标进入对外披露或结算 | 口径管理是必需项,且必须有人担责 |
判定线:当一个数会被比较、被跨团队引用或带来后果时,口径管理才值得投入。其余情况把时间花在把分析做对更划算。
自测
- 两个团队报的同一指标差三成,你按什么顺序排查四个层次,为什么这个顺序能省时间?
- "哪个口径对"为什么是伪问题?换成什么问题之后争议才会真正收敛?
- 一个口径缺少"异常剔除"这一项,会在什么业务阶段先出问题?
- 口径硬切不标注,三个月后最可能被误读成什么?
- "以财务口径为准"为什么有效?这种裁定在研发内部指标上为什么常常失效?
- 口径字典最常见的失效方式是什么?用代码生成能解决其中哪一部分?