2019年,我因为 456 单的差异,被运营总监在群里 @ 了三天
那时候我在一家做跨境电商的公司,深圳,团队大概 200 人,我在数据组,一共 4 个人,算上我。每天早上 9 点半到工位第一件事,打开 Metabase 看昨天 GMV 的看板,然后截图发到运营大群。这个流程跑了大概半年,一直很平静。
直到有天早上,运营总监在群里 @ 我,说他们后台昨天看到的是 12,847 单,我发的看板是 12,391 单,差了 456 单,问我是不是看板有 bug。我当时第一反应是“不可能”,因为那条 SQL 我自己写过两遍。第二反应是心虚,因为 Metabase 里那个查询是上一个同事留下的,我其实没逐行看过。
后来的三天,我基本没干别的。白天在 Hive 里跑数,晚上跟运营那边对表。结论是:不是 bug,是口径。而且不是一种口径问题,是四五种叠在一起。
那 456 单,具体差在哪五个地方
我把当时的排查记录翻出来了,大致是这么几条,写出来给后面的人省点时间。
时区。 运营后台用的服务器本地时间是 Asia/Shanghai,我们数仓 dwd 层入库统一用 UTC。Hive 分区 dt='2019-08-13' 实际覆盖的是北京时间 8 月 13 日早上 8 点到 8 月 14 日早上 8 点。这一条大概贡献了 200 多单的差异,因为那家公司主要市场在东南亚和欧洲,订单集中在北京时间下午到凌晨。
取消单和退款单。 运营后台的“订单数”是下单口径,用户点了提交就算一单;我们那张表是支付成功口径,status in ('paid','shipped','done')。那阵子他们在跑一个大促短信召回,领券下单的人多,取消率比平时高了差不多 6 个点。
拆单。 业务库的 order 表有拆单逻辑,一个用户下单 3 件商品,如果分属不同仓库,会被拆成 3 条子单,但共用一个 parent_order_no。我在 SQL 里写的是 count(*),不是 count(distinct parent_order_no)。
测试单。 这是最蠢的一条。我们没加 and is_test = 0,也没排除内部账号。研发那边每天跑自动化测试往生产库写单,日常几十条,那天他们发版,多写了一百多。
跨天支付。 北京时间 23:58 下单、00:03 支付成功,这单在“下单口径”算前一天,在“支付口径”算后一天。单量小,但每天都有。
五条加起来,方向不完全一样,有的加有的减,最后净差 456。
所以我后来不太建议新人一上来就啃 Python
这事之后我有个变化:不太劝刚入行的朋友先去学 pandas 和 sklearn 了。
不是说没用,是说性价比的问题。做数据分析,实际工作里可能有七成时间在处理“数从哪来、口径是什么、为什么对不上”,剩下三成才是算。工具上我大概会这么排:
10 万行以内的数据,Excel 透视表加几个 SUMIFS 就够了。xlsx 单表行数上限是 104 万行,但实际到 20 万行就开始转圈,到 50 万行基本别想动。这个量级硬上 Python 反而慢,因为你写脚本、配环境、导出再导入的时间,比在 Excel 里拖两下长。
百万到千万级,直接上 OLAP。ClickHouse 单机一张 5000 万行的宽表做 group by 聚合,几秒钟出结果,机器配置别太差就行。Doris、StarRocks 也差不多。这时候别用 MySQL,MySQL 到千万级做 group by 会开始往磁盘写临时表,等的时间够你下楼买杯咖啡。
Python 的位置在哪?在“结果要二次处理”的时候。比如做留存曲线、做 cohort、跑个简单回归看变量相关性,SQL 写起来会很别扭。pandas 单机处理 500 万行、三四十列的表,内存占用大概 2 到 4 个 G,read_csv 的时候把 usecols 和 dtype 加上,能省掉一大半内存和一半时间。这个技巧我是被 OOM 杀过三次进程之后才记住的。
那张口径表,我们维护了两年,然后没人看了
事情解决之后,我建了个飞书表格,叫“指标口径说明”,第一版 30 多行。字段是:指标名、业务定义、技术口径(一段 SQL)、数据源表、时间字段、时区、过滤条件、负责人、最后更新时间。
2020 年它涨到 137 行。2021 年涨到 200 多行,开始有人往里塞自己部门的口径。2022 年,它变成了一个没人打开的表,因为新来的同事不知道它存在,知道的同事觉得里面的东西“肯定过时了”。
我现在觉得问题不在表本身,在于没人负责更新,也没有任何流程强制你去更新。口径文档这东西,靠自觉是活不下去的。
如果重来一次,我会做两件不一样的事:第一,把口径写在 SQL 注释里、写在 BI 工具的指标定义里,让它出现在大家一定会看到的地方,而不是一个独立文档;第二,把指标数量砍掉七成。
一个可能不太受欢迎的结论
一个 30 人左右的业务团队,真正需要每天看的核心指标,我觉得不该超过 15 个。但我们当时看板上有 80 多个。80 多个指标的后果是,每个人都有自己关心的那 5 个,开会的时候各说各的,谁也不算错。
我待过 3 家公司,数据分析师每周花在“为什么这个数和那个数不一样”上的时间,我估一下,大概 1.5 到 2 个工作日,接近 30%。这个数字挺吓人的,但它不是靠买更贵的 BI 工具能解决的。
关于埋点,说几句可能得罪人的
埋点数据和业务库数据几乎永远不会完全对上,别追求 100%。
前端埋点会被浏览器插件拦截,uBlock Origin 这类工具在部分用户群里能拦掉 10% 到 30% 的采集请求。App 端丢的来源更杂:卸载、断网、进程被杀、SDK 初始化失败。我见过最夸张的一次,Android 某个版本的埋点丢了 40% 的启动事件,原因是 SDK 在冷启动时做了个同步网络请求,超时了。
所以我的用法是:看趋势、看比例、看漏斗的相对转化,用埋点;看绝对值、看钱、看财务能对上的数,用业务库。这两个东西不是互相替代,是互相校验。如果你只有一个数据源,那它错了你也不知道。
顺便一句,GA4 的会话默认超时是 30 分钟,后台可以改,但改了之后历史数据不会重算。这类“改了不回溯”的设置,做口径对比之前一定要先确认。
最后
我不太喜欢“数据驱动”这个词,感觉被用烂了。我自己的经验是,数据能帮你排除掉一些明显的错误答案,但选哪个答案,通常还是靠人的判断,而且经常是拍脑袋。承认这一点,反而能让你把精力放在更值当的地方,比如先把口径对齐。
先聊这么多。