看板有漏斗阶段,为什么转化率仍可能是假的?用 ClickHouse 检查演示埋点

部署开源埋点分析系统后,最容易让人误判的画面,是看板已经显示"打开应用 → 浏览商品 → 加购 → 购买"。四个阶段都有数,似乎意味着用户转化漏斗已经跑通。但事件分布图、阶段用户数和按同一用户、按时间顺序计算的转化漏斗,是三种不同的东西。

本文用 SensorFlow 的公开 Demo 数据生成脚本做一个反例。它能帮新用户确认 ClickHouse 与 Superset 的演示链路,不是生产 SDK 的真实行为样本。我们直接读脚本、运行 SQL,看看一个"四阶段看板"到底证明了什么。

看板显示四个阶段,是否意味着真实用户完成了漏斗?

不能。 四个事件名都有记录,只证明每个阶段分别出现过;即便每个阶段都有去重用户,也不能证明有同一个用户依次完成这些阶段。真正的漏斗还需要固定用户标识、时间窗口、事件顺序、时区、去重和重试规则。

SensorFlow 的演示阶段由 ./install.sh 导入标记为 app_id = 'sensorflow-demo' 的合成事件,并创建 Superset 图表;此时真实神策 SDK 接收服务尚未启动。需要真实接收时,应按中文快速开始另行激活许可证。演示看板只能用于检查部署与可视化,不应拿来证明业务转化率。

先看数据是怎样造出来的

Demo SQL从 numbers(240) 产生 240 条记录。事件由 number % 12 决定,用户由 number % 48 决定。因为 48 是 12 的倍数,同一个 demo-user-* 在这批样本里总落在同一类事件,而不是跨阶段移动。

它还把事件时间按每条间隔 3 小时回推,并给购买事件设置演示收入。is_first_day 只是生成脚本设置的示例值,不是生产环境中经过身份合并后计算的"真实新用户"。这些设计适合展示图表形状,但不足以验证同一用户的完整转化路径。

从表结构可见,事件表叫 sensors.event,关键字段有 time、event、distinct_id、app_id。先查询各阶段的行数与去重用户数:

sql 复制代码
SELECT
    event,
    count() AS event_rows,
    uniqExact(distinct_id) AS users
FROM sensors.event
WHERE app_id = 'sensorflow-demo'
GROUP BY event
ORDER BY event;

在当前脚本首次、只导入一批 240 条 Demo 事件 的情况下,四个阶段分别是:打开应用 100 行/20 人,浏览商品 80 行/16 人,加购 40 行/8 人,购买 20 行/4 人。这里的"人"是 distinct_id 去重结果,不等于实际客户人数。若你运行的是旧版脚本或已有演示数据,结果可能不同;请以自己表里的记录为准。

再检查"浏览过的人"与"买过的人"是否重叠

最小的下一步不是马上算 4 / 16 = 25%,而是查同一 distinct_id 是否同时出现两个事件:

sql 复制代码
WITH per_user AS
(
    SELECT
        distinct_id,
        countIf(event = 'demo_product_view') > 0 AS viewed,
        countIf(event = 'demo_purchase') > 0 AS purchased
    FROM sensors.event
    WHERE app_id = 'sensorflow-demo'
    GROUP BY distinct_id
)
SELECT
    countIf(viewed) AS view_users,
    countIf(purchased) AS purchase_users,
    countIf(viewed AND purchased) AS both_users
FROM per_user;

按当前生成脚本,结果是 16 / 4 / 0:16 个浏览用户、4 个购买用户,同时满足两者的用户为 0 。所以 4 / 16 不是这批样本的"浏览到购买转化率"。上述 SQL 只检查用户交集;即使交集大于 0,它仍没有证明"先浏览、后购买",还要进一步加入时间顺序与窗口。

这里刻意使用 ClickHouse 的 countIf 条件聚合和 uniqExact 精确去重。大规模生产数据是否选近似去重,需要再按内存与精度要求评估,不能把 Demo 查询性能当成压测结果。

真正的漏斗至少多出三个约束

第一是同一人 。匿名 ID 和登录 ID 是否合并?distinct_id 是否跨设备稳定?若身份规则变化,UV 和漏斗分母都可能变化。

第二是顺序与窗口 。浏览、加购、购买必须按定义顺序发生,且全部落在约定时间内。ClickHouse windowFunnel 文档描述的正是时间窗口内的事件链计算;但窗口单位取决于时间戳参数类型。SensorFlow 表中的 time 是 DateTime64(3),不能未经验证就把现成秒级示例直接套到毫秒字段上。

第三是口径隔离 。看板和 SQL 应统一过滤 app_id,把 sensorflow-demo、测试环境和真实生产流量分开。购买事件里的演示收入不应进入真实 GMV;测试账号的事件也不应算入运营报表。

当这些约束确定后,再用少量人工可核对的真实 SDK 测试事件验证整条链路:客户端网络请求成功、接收端处理成功、ClickHouse 原始记录正确、Superset 指标与 SQL 一致。神策 SDK 接入说明列出了需要检查的接收地址和兼容边界。不要用演示图表替代真实事件验证。

一个可执行的验收顺序

  1. 看仓库中的 Demo 生成 SQL,确认它是否代表真实用户旅程;不要只看图形名称。
  2. 用 count() 看行数,用 uniqExact(distinct_id) 看各阶段用户数,先分清事件量与人数。
  3. 用按用户聚合的 countIf 检查阶段交集;没有交集时,不计算转化率。
  4. 用人工构造的跨阶段测试用户,再验证事件先后顺序、时间窗口和身份合并。
  5. 将 Demo、staging、production 分开,在 Superset 中复核与原始 SQL 相同的过滤条件。

结论很简单:演示看板验证的是系统可展示,真实漏斗验证的是数据语义。 对已有神策 SDK 的团队,SensorFlow 的价值在于能把事件送到自有 ClickHouse、让工程团队直接检查原始记录;代价是团队仍要定义指标口径并运维 Go、ClickHouse 和 Superset。若你更需要现成的完整产品分析界面,而不打算维护 SQL 指标,应另外评估专门的分析产品。

想复核本文的例子,直接从公开仓库的 Demo SQL开始,先跑两条查询,再决定是否激活真实 SDK 接收。

相关推荐
高频因子挖掘机1 小时前
本地已有一年历史数据,第二天更新别只拉“昨天一天”:量化 K 线增量同步实践
后端·github·api
高频因子挖掘机1 小时前
只补缺失日期,不重拉全部历史:增量更新 K 线应该怎么设计?
后端·github·api
Gopher_HBo2 小时前
Go reflect 包动态调用与方法反射 —Call、MakeFunc 与性能
后端
码事漫谈3 小时前
人设带处境,AI 更入戏
后端
掘金酱3 小时前
【10.24 特别活动】AI 时代,程序员的新饭碗在哪里?
前端·人工智能·后端
leeyi3 小时前
缓存同步之后,还要考虑没收到消息的节点
后端·性能优化·erlang
IT_陈寒3 小时前
Redis的原子性竟然失效了?记一次分布式锁翻车实录
前端·人工智能·后端
shengjk13 小时前
既然 gRPC 底层也是 HTTP,为什么大厂还非要死磕 RPC?真相全在这!
后端
回家路上绕了弯3 小时前
人和 Agent 怎么一起干活:把任务交出去,把判断留在关键处
后端
波力海苔夹心脆6754 小时前
C# 正则表达式零基础入门:从“看得懂“到“会用“
经验分享·后端·mysql·正则表达式·c#