本期视频发布于 2022 年。文中的技术状态、个人经历与观点均为当时情况。
一次点击从客户端进入数据仓库,中间会发生什么?它为什么有时迟到,有时缺字段,甚至在不同团队的报表里代表不同的含义?张彦瑞与武蕴在这场 T Chat 中,把这些看起来零散的问题放回同一条数据链路里讨论。
两位讲者在当期活动海报中以知乎数据链路团队的身份参与。第一段围绕埋点体系的建设与治理,第二段讨论 A/B 实验的客户端设计。前者解决行为数据怎样可靠地产生、传递和解释,后者进一步追问:用户到底接受了哪个实验方案,记录下来的数据又能否支撑判断。第一段 00:03 · 第二段 00:02
从 SDK 到数仓,数据会经过多次加工
分享先展开知乎当时的埋点链路:Web、H5、小程序和移动应用通过各端 SDK 上报,日志接收服务将数据送入 Kafka 等消息队列,经过 ETL 加工,再进入 Hive、Doris 等存储与查询环节,供下游业务使用。推荐和机器学习等实时服务,与偏离线的数据分析,对到达时间和处理方式有不同要求。
他们介绍,当时实时通道与其他日志通道分别承载不同业务。接收侧还需要应对下游故障:例如消息队列暂时不可用时,先用 LevelDB 缓存数据,待通道恢复后继续发送。与此同时,两条通道存在部分数据重复,因此讲者提出,未来可以考虑合并上游接收,再向下游分流。这是分享中的后续设想,不能当作已经完成的架构改造。第一段 00:30–06:35
数仓里的字段也不一定全部由客户端直接填写。例如 App 启动很早时,还没有拿到用于时间校准的网络时间;ETL 可以结合随后上报的设备信息和时间坐标,尝试补齐早期日志。另一些字段则来自业务规则:某类点击或页面曝光是否代表活跃行为,可以在下游统一计算。对客户端开发者来说,理解这些加工过程,才能解释为什么查询表里会出现自己没有直接上报的字段。第一段 02:49–05:41
数据模型决定后续治理能走多远
接下来,两位讲者把重心转向数据模型。SDK、传输协议、ETL、数仓表和消费方都会依赖它;上线之后,修改模型往往意味着跨团队迁移。单个团队未必能获得足够直接的收益,却需要共同承担改造成本,因此实际系统很容易不断增加字段,而难以重新整理结构。
这里有一个重要范围:分享随后展开的模型,是讲者结合不同工作经历总结的设计思路,并非每一项都对应知乎当时已经部署的内部实现。它的价值在于说明,怎样让采集、规则校验、自动生成代码和数据分析共享一套稳定的语义。第一段 06:35–10:20
他们用 Who、When、Where、How、What 拆解一次行为。用户和设备身份、事件时间、环境信息,以及从推送或外链进入应用的方式,大多适合由 SDK 统一管理;业务开发者主要描述对什么对象做了什么。时间也不只有一个:事件产生、客户端发送和服务端接收的时刻,分别帮助解释本地积压和传输延迟。把公共上下文统一补齐,才能减少不同业务各自实现时的口径偏差。第一段 10:20–12:58
用资源层级把页面、模块和动作组织起来
针对业务行为,讲者提出 App、Channel、Page、Module、Action 的层级:应用之下区分业务归属,再定位页面、页面中的模块以及具体动作。页面曝光、按钮点击、滚动或编辑,都应当能落到明确的位置和含义上。
Channel 不只是一个分类字段。它可以帮助确定成本归属、质量问题由谁处理,以及分析结果属于哪个业务。当数据规模继续扩大时,这个维度还可能参与消息处理和存储资源的划分。不过,讨论中的分部门路由属于设计思路,文章不把它写成知乎当时已经实施的方案。与此同时,模型也需要给崩溃、系统事件等难以套入页面结构的记录保留出口,不能为了整齐而丢失有价值的数据。第一段 12:58–22:59
有了这些元数据,埋点平台才能知道一条日志应当包含哪些字段、哪些值有效。否则,平台只能检查数据有没有送到,无法判断业务含义是否正确。讲者也讨论了行业能否形成更统一的模型,但那是一种协作愿望,并不是已经存在的通用标准。
页面实例和模块继承,让一条点击带着上下文
在这个模型里,页面名称与一次具体的页面曝光是两回事。同一个页面反复进入,需要分别识别每次访问。分享用 RequestID 表示页面的一次曝光实例:页面出现和离开使用同一个标识,模块事件也继承这个实例上下文。这里的 RequestID 不能简单理解为一次网络请求的编号。第一段 18:09–23:24
业务侧提供页面 ID 和内容等参数,SDK 补充实例标识、时间、会话与来源信息;离开页面时,再计算这次停留的时长。下游可以把离开事件中的时长关联回曝光记录,让分析者更方便地查询完整的一次访问。另一种做法是等页面结束后只发送一条记录,传输和处理更简单,但如果离开事件丢失,这次曝光也可能无法留下。因此,拆分曝光与离开事件,是可靠性和处理复杂度之间的选择。第一段 23:24–26:59
模块点击同样可以继承页面的内容 ID、页面 ID 和曝光实例,避免业务重复填写。关联也可以放到数仓完成,但实时推荐希望尽早拿到完整上下文,就更有理由在客户端先做合并。讨论的关键不是把所有工作固定放在某一端,而是看数据的消费者何时需要这些信息。第一段 26:59–29:16
Session 的边界,需要先说清业务口径
用户切到聊天软件回复消息,随即回到原应用,是否应该开始一个新会话?如果只按前后台切换更新 Session,连续的行为会被切得很碎。讲者因此讨论了超时机制:用户在一段时间内没有行为,再回来才视作新会话,而具体时长应当适配应用类型。
但时间不是唯一条件。如果用户是被新的推送或外链重新召回,即使间隔不长,业务也可能希望开启一个新会话,以分析这次召回是否有效。这意味着 Session 同时承载行为连续性与来源归因的取舍。不同公司、甚至同一公司的不同部门,可能需要不同口径;先定义清楚,再让采集和分析保持一致,比把某个超时值当成标准更重要。第一段 29:16–33:16
路径染色:记录一次转化经过了哪里
站内归因部分讨论的是,怎样把关键路径带进后续事件。一笔下单可能经过首页、内容页和某个按钮,也可能从另一条入口直接到达。若只保存上一个页面,分析者很难还原更早的点击。路径染色让后续记录携带此前的关键节点,从而支持路径比较和漏斗分析:用户在哪一步减少,哪些入口出现在最终转化路径中。第一段 33:16–35:39
路径也不能无限增长。讲者举例,用户从第二个页面进入第三个页面,随后返回第二个页面再继续下单,可以按既定规则把这次绕行从关键路径中去掉。SDK 负责页面与点击等基础路径,业务还可以加入自定义标签,标记某个按钮或特定活动入口。最终,路径与标签一起进入数仓,通过查询或 UDF 等方式提取分析信息。第一段 35:39–39:04
这让业务能够检查某种入口与成交路径的关系,也减少反复拼接日志的工作。这里的归因依赖预先定义的路径规则;某个按钮出现在转化路径中,并不等于已经通过实验证明它带来了额外转化。第二段对 A/B 实验的讨论,正好补上了“看见路径”之后如何判断改动效果的问题。
四类埋点方案,没有省掉所有成本的选项
讲者比较了全埋点、可视化圈选、声明式和手动埋点。全埋点尝试通过拦截系统事件自动采集,业务接入轻,但采集范围容易超过真正的需求。系统控件名不一定等于可分析的业务名称,流量和后续加工成本也会增加;输入框等控件还要求额外处理,避免收集不应采集的信息。第一段 39:04–41:28
可视化圈选把范围收窄到关心的控件,却往往依赖视图层级。一次没有改变产品功能的界面重构,也可能使既有规则失效。声明式方案则为控件扩展属性,让业务填写页面或模块等信息,由 SDK 管理触发时机;代价是基础设施需要持续覆盖新控件和新系统能力。手动埋点最灵活,可以精确表达业务动作,但会增加业务代码与维护工作。第一段 41:28–43:57
两位讲者介绍,知乎当时结合了声明式与手动方式,并让 SDK 承担页面停留等公共行为的处理。手动方式的成本也可以通过工具降低:当模型和规则已经明确,管理平台便能生成示例代码,验证字段,而不必让每个开发者从头理解整套协议。方案选择因此离不开配套平台,不能只比较第一次接入需要写几行代码。第一段 43:57–45:20
上报策略既要保护数据,也要保护服务
不同日志对业务指标的重要程度不同。分享中的方案按高、普通和低优先级管理事件:应用级事件关系到整体活跃指标,页面曝光和点击关系到具体业务,模块曝光等数据则可以有不同的调度优先级。客户端使用本地缓存,并通过分表等方式降低读取相应日志的成本。公共环境字段还可以合并,减少同一信息在每条记录里反复传输。第一段 45:20–48:15
稳定性方面,两位讲者强调要感知服务端压力。当接收端持续报错时,客户端通过指数退避降低请求频率,避免重试继续扩大压力。埋点是大量主动上报的场景,不能沿用只考虑正常业务请求的思路。
当本地已经积压多批日志,仅靠固定轮询又会恢复得很慢。因此可以连续发送已有批次,同时保持同一时刻只处理一个批次,兼顾追赶进度与并发控制。这里没有一个适用于所有公司的固定批大小;重要的是把平时上报、故障退避和恢复追赶放在同一套策略中设计。第一段 48:15–50:11
质量监控需要知道自己的估算边界
怎样证明 SDK 没有漏掉日志?讲者指出,业务数据本身也依赖埋点,不能轻易充当另一套完全可靠的对照。他们介绍了利用日志序列号估计应有数量,再与实际去重上报量比较的办法。由于不同优先级事件可能以不同顺序到达,可以借助已经到达的序列范围观察缺口。
这仍然不是精确的总体成功率。如果更后面的日志也没有到达,观察到的最大序号就会偏小,估计可能过于乐观;如果日志严格按序到达,这种思路也难以暴露尚未出现的缺口。因此它更适合看变化趋势、发现异常,而不是证明数据百分之百完整。第一段 50:11–52:10
延迟监控则利用事件产生、发送和接收时间,区分本地积压与到达服务端的时间差。跨设备时间需要考虑校准条件,不能把两端时间戳简单相减就称作精确往返时延。除这些指标外,分享还涉及设备标识发放、关键字段空值、日志量的周期变化,以及配套的质量报警。第一段 52:10–53:33
讲者用几个 SQL 场景说明,移动端开发也可以直接参与数据检查:按积压时长分组计数,分析版本或信息来源的分布,以及检查同一个参考设备标识是否对应多个内部标识。案例的共同点,是先明确观察口径,再用条件聚合、分组和筛选定位问题。无需一开始就成为数仓专家,也能用这些查询检验自己的 SDK 行为。第一段 53:33–56:37
管理平台把需求、研发与验证连成闭环
第一段最后回到团队流程。需求方提出行为分析诉求,数据产品同学设计埋点,研发判断能否实现;不能实现的设计需要退回调整,而不是在上线前临时用一个相似字段代替。开发完成后,依据模型与录入规则进行测试,再经过发布、灰度和回归。第一段 56:37–57:56
管理系统保存页面、动作语义和自定义参数,生成示例代码;验证工具则抓取实际日志,按选定语义检查字段和值,并生成报告。前面反复强调的统一模型,到这里才体现出实际收益:需求、实现和验收可以围绕同一个定义交流,减少数据产品、业务研发和测试之间的重复解释。第一段 57:56–59:21
A/B 的客户端底线:同一实验不要串组
第二段从实验值的稳定性讲起。对同一个实验单位,实验进行与放量过程中应当保持分组稳定;否则,用户先后接受不同方案,后续就很难解释观察到的行为属于哪一组。第二段 00:02–01:32
讲者区分设备维度和用户维度。前者依赖设备标识,后者依赖用户身份;退出登录、切换账号后,不应沿用前一位用户的实验结果。与此同时,分流请求是异步的,业务可能在结果到达之前就读取配置。这套方案会给出默认值,并将这类尚未进入有效实验分组的读取与正式实验曝光区分开。
更细的一点是,某个实验第一次读取已经得到默认值,后续结果到达,也不立即改变这个生命周期内已经用过的值;尚未读取过的其他实验,则可以使用新拿到的结果。这种处理优先保证已经发生的体验不在中途改变。默认组不按正式实验曝光上报,是这套实现的规则,不意味着仅凭这一条规则就能证明整个实验不存在偏差。第二段 01:32–04:29
分流缓存与曝光缓存,解决的是两件事
实现流程先等待设备或用户标识具备,再请求分流结果。请求失败可以重试,也可以像日志上报一样加入退避,避免服务端承压时持续高频请求。成功后,把结果放入内存,并保留磁盘缓存。第二段 04:29–06:31
业务真正读取某个实验时,系统先查“已经曝光过什么”的缓存。如果已有记录,就继续返回这个值;没有时,才从分流结果中取值,记录曝光并固化到曝光缓存。于是,服务端返回了一组配置,与用户已经实际使用了其中某个实验,是两个不同状态。
用户维度的实验还需要在退出登录时清理对应的曝光状态,让新身份拥有独立的记录。这个结构的意义在于,把首次使用和之后重复读取分开处理:配置可以更新,已经生效的体验则按约定保持稳定。第二段 06:31–08:06
实验曝光也需要可靠上报
分流结果稳定且没有变化时,讲者介绍了利用 ETag 一类缓存校验机制减少重复传输的思路。多个实验在相近时间内被命中,也不必分别产生请求,可以合并成批次上报,降低请求频率。第二段 08:06–09:34
更重要的是,用户已经接受实验方案,但曝光日志没有到达,分析侧就会少算这次经历。方案因此在发送前先落本地存储,成功后删除;失败时保留,利用后续生命周期机会继续补报。前半场的缓存、重试和丢失率问题,在实验系统中再次出现,只是此时数据质量直接影响我们如何解释实验结果。第二段 09:34–10:32
从空转检查到公司级指标,技术实现之外还有判断
最后,两位讲者补充了实验分析的两个议题。第一是正式施加不同方案之前的“空转期”:先完成分组,在各组接受相同体验时观察指标,检查是否存在明显的不均衡。若分组本身就带有差异,后来的结果不能轻易全部归因于产品改动。空转是检查环节,并不能单独替代完整的实验设计和统计分析。第二段 10:32–11:59
第二是局部收益与整体收益的冲突。某个首页实验可能提高 A 业务的指标,却损害 B 业务。分享用“北极星指标”讨论公司共同认可的核心衡量标准:是否推广实验,需要回到整体目标,而不能只由发起实验的团队宣布胜利。这是他们对决策口径的介绍,并非给出一套适用于所有组织的指标清单。第二段 11:59–13:37
两位讲者也明确,分流算法、指标计算、实验准备和报告分析还有专门的工作,短时间内无法展开。客户端这一侧能做的,是先把身份、分组、实际曝光与上报质量管理好,让后续分析有可信的输入。第二段 13:37–14:25
这也把两段分享连在了一起:事件模型决定数据如何被解释,采集与上报决定观察是否可靠,实验设计再决定能从这些观察中得出什么结论。治理工作贯穿整条链路,需要各环节对同一件事情有一致的定义。

