在字节,大规模埋点数据治理这么做!
字节跳动内部统一的埋点平台——流量平台,已经覆盖了字节内部 2000 多个应用,管理埋点(事件)数 20 万,每天产生的埋点数据量超过万亿,每年能给公司节省的成本超亿元。本文整理自字节跳动数据平台——流量平台技术负责人 Cody 在火山引擎开发者社区 Meetup 第四期演讲。
埋点是什么?埋点主要是描述用户在 APP 内触发的一系列行为,包括点击、侧滑等。基于这些行为,我们可以进行行为分析、个性化推荐、精准营销等很多事情。埋点主要描述的是哪些数据?
Who:谁操作的数据
When:什么时候操作的数据
Where:在哪些页面、模块的数据
How:用户如何操作的
What:有哪些附加信息
因为本文介绍的是埋点治理,所以这里再介绍一下什么是数据治理。
数据治理是指在数据的生命周期内,对其进行管理的原则性方法,其目标是为了确保数据的安全、及时、准确、可用和易用。数据总是会变得无效甚至无用,因此就涉及到对存量数据的治理。但这里要强调一下,数据治理不只针对存量数据,更重要的是对增量数据的治理,通过一系列手段,能保证数据从源头开始就是正确的。此外,所有的治理都有具体的落地内容,一个稳定的治理链路是所有数据治理的基石。
下面就为大家介绍字节跳动是如何治理埋点数据的。
字节跳动流量平台
流量平台是字节跳动内部统一的埋点平台,覆盖埋点数据定义、采集、生产、应用、治理等埋点全生命周期。当前,流量平台已经覆盖了 2000 多个应用,管理埋点(事件)数 20 万,每天产生的埋点数据量超过万亿,每年能给公司节省的成本超亿元。
图注:字节跳动流量平台的产品概念图
字节跳动流量平台主要分为几块:
埋点内容:这是用户接触最多的一块,包括埋点生命周期的设计、开发、验证、上线、使用、乃至下线。
埋点治理:指的是狭义的对存量数据的治理。涉及成本、SLA 等的治理。
链路侧:包含埋点的收集、处理和订阅的全链路,平台目前支持对 iOS、Android 等全端数据的收集。平台已经跟下游使用流量数据的应用进行打通,用户可以订阅数据。
链路根基:即自研的动态实时计算平台,也是整个平台的核心技术,它能够支撑起字节跳动万亿+的实时数据的处理。
埋点内容解决方案
埋点内容主要管理埋点生命周期,这里要着重强调一下上图中心位置的埋点模型其实非常重要,因为埋点模型设计的好坏直接影响到埋点的设计、开发、测试甚至使用。
埋点内容用户痛点
埋点内容的用户主要是有两大类:埋点消费者和埋点生产者。对于埋点消费者来说,存在如下痛点:
查找难度大:埋点数量非常多,找不到自己想要的埋点。
使用难度高:找到埋点之后,用户也很可能不清楚指标埋点口径。
埋点难信任:当数据不符合预期时,不确定埋点数据是否可用。
对于埋点生产者来说,也有一些痛点:
生产链路长:各方信息对齐、流程推动难度大;
模型落地难:不知如何设计、不知是否符合规范;
缺乏工具支持:设计、开发、测试纯白手起家。
那怎么去解决用户的这些痛点呢?首先我们要弄清楚埋点的第一站是什么。很多公司都有埋点系统,对于大部分公司而言,埋点的第一站是埋点录入。但是大家会发现,埋点录入并不是一切的源头,埋点设计才是。埋点设计是第一手的资料,根据埋点的设计文稿可以将用户的需求梳理得非常细致。而埋点录入是第二手甚至是第 N 手资料,录入的信息肯定会有丢失,并且只能进行一些基本的校验,满足基本的准确性。
其次,如果没有资产的辅助设计,每一个埋点录入都要从 0 到 1 去实现一遍。但是埋点设计通过资产辅助设计可以变得很简单。因此,我们认为埋点设计才是 the single source of truth,这是我们整体设计的核心。
下面来看看用户如何在我们的系统设计埋点。
字节跳动流量平台的产品辅助设计基于灵活的模型支持、设计资产积累、设计辅助,可以方便每一个用户定义高质量数据,让用户愿意在系统进行设计和评审。
设计完埋点,我们在埋点开发上也有对应的工具来服务来发。
当用户要设计埋点时,可以通过 ID 找到要开发的埋点,通过点击即可插入代码。同时,系统支持 VSCode 等主流编辑器,针对不同语言和代码风格自定义代码模版,还有类型校验、编辑切换等。
埋点测试
埋点测试比 QA 要难很多,看的是一串数字、类型的值等。在字节跳动流量平台系统中,可以依托埋点设计中的规则辅助测试,针对类型、取值、必填等自动验证,并且可以一键生成报告。
我们是怎么去做好测试这件事的呢?重点还是前面提到的做好埋点设计。只有设计周全,才能积攒足够的规则进行自动化测试,因此埋点设计方案非常重要。
埋点设计者会在方案设计时制定一系列的约束规则,我们会依托这些约束规则生成一系列相匹配的测试用例,并在测试过程中进行自动匹配、测试。
埋点测试时,测试者手机扫码即可将服务器和浏览器建立连接,在 App 上操作后,流量平台可以实时接收到对应的埋点数据。因为已经有测试用例,规则执行引擎便可以自动匹配执行并得到结果,再通过验证结果推送服务实时推送至浏览器。
埋点测试后,用户可以通过报告生成器可以一键生成报告,发送给 RD 进行修改或者 DA 进行验收。这样就完成了整个测试流程。
埋点存量治理
在生成了大量的埋点之后,我们需要进行存量埋点数据的治理,具体涉及 SLA、成本、合规以及数据质量等方面。为什么还要进行存量埋点数据的治理呢?我们有这样一些观点:
数据不一定都是重要的。因为业务都不一定总是重要的。
数据并不总是有用的。比如活动下线了,埋点就要下线,否则付出了成本却没有收益。
数据不一定总是合规的。随着数据隐私的重要性越来越高,合规要求也在不断更新变动。
对于存量埋点数据的治理,也有一些痛点。对于治理负责方来说,数据越来越多,而对数据的实时性要求却越来越高;随着数据量暴增,成本也急剧增加,SLA 等级越来越慢;用户隐私也越来越重要。对于治理实施方来说,他们可能不敢治理,不愿治理,也不懂治理。
我们梳理了用户的需求,发现是这样几层:
用户层:自动化识别埋点是否有用,流程化引导埋点的分级和下线。
统计层:做到数字化、货币化,治理大盘清晰可知,埋点成本准确无误。
甄别层:通过的血缘图谱,对实时统计、离线报表、行为分析、推荐系统等做实时决策。
执行层:从 APP 端到数仓全流程,强兜底。
链路层:需要高效、稳定、完整的链路方案解决治理难题。
针对这些需求,我们是怎么做的呢?
埋点分级/无用埋点甄别
埋点血缘和离线血缘抽取不太一样。离线血缘是点与点之间的血缘,但埋点血缘关注的是内容与点的血缘,
它需要知道一张表的哪些行的信息有用。这是完全不同的一个领域,没有任何前人的经验可以借鉴,我们在埋点血缘做了几个方面的事情:
离线处理:通过 SQL 解析,计算埋点与离线表的血缘;
实时处理:利用埋点链路优势,掌握实时分流血缘关系;
即时分析:与行为分析、SQL 临时查询系统打通;
推荐系统:利用数据清洗链路优势,解耦推荐血缘。
在埋点分级上,我们以性能埋点为突破口,给予匹配的 SLA 和 TTL 配置。
埋点链路解决方案
下面着重介绍一下我们的埋点链路解决方案。首先我们还是来看下埋点链路用户的需求是什么。对于非技术的运营、分析师同学,他们需要清楚自己:
需要什么样的数据:是埋点数据还是展现数据?
需要哪些数据:最好能对不同来源不同时效性的数据进行可视化过滤、清洗。
需要在哪里用这些数据:是实时报表,还是行为分析,还是推荐?
埋点链路的挑战
在埋点链路设计上我们也遇到了一些挑战:
大数据量下的稳定性:埋点数据在字节跳动是核心数据,其稳定性非常重要。
低延迟实时处理:尤其对于推荐,实时性要求非常高。
分级构建+下线,这里分几块内容:
数据接入:我们提供全栈 SDK 接入,还在 SDK 内置了管控的机制,利用各个 APP 内终端计算的能力,大大节省成本;而且根据合规要求不能上报的埋点就可以直接在 SDK 端丢弃掉。
数据收集:数据收集一般是提供 HTTP 接口,将上报的数据存到消息队列。而埋点数据量特别大,于是我们进行了埋点聚合,将埋点的 Event 数据聚合成 Applog 数据一起上报。数据进入到 Applog 后通过自研的实时数据处理平台来解析。
实时动态引擎处理
上图是我们自研的实时数据平台架构,该平台主要解决两方面的问题:
实时处理:快速处理大量数据;
动态化:字节跳动服务规模巨大,重启时间过长会导致下游断流,因此我们的实时领域里不接受重启。此外,逻辑有任何修改都要重启,在大量的业务和逻辑下这是不可能实现的。所以该平台一定要做到动态化。
动态实时处理引擎在收到实时数据的 Applog 后将其解析成真正的埋点数据。再通过数据加工,可以转换为其他的(甚至自定义的)数据格式,最后通过数据订阅推送到各应用。
第二种模式是分级。在埋点数据被解析以后,我们会打上标记,然后 dump 到 hdfs 不同的路径下。后续 Hive 进行构建的时候可以区分优先级,优先级高的进入高优队列。Hive 的数据也是分区的,分区的数据可以制定不同的 TTL。这样,数据的 TTL 和 SLA 就都能分级了。
第三是强保证。在埋点数据下线前,先将要下线的数据分流到 pre-discard Hive 表中暂存 30 天。如果在这段时间里没有问题,30 天之后就可以直接下线。
现在,该引擎的处理逻辑、拓扑、函数以及 RPC 都可以做到动态化。用户对于上游而言,一般是写 SQL 或者进行界面化操作。因为用户不懂如何处理,我们就需要特定的模型让用户进行适配。于是我们用声明式表达建立统一的逻辑模型让用户直接适配。在引擎上我们还能以插件化的形式支持 Flink、Pyjstorm、TCE 等多种运行时平台,业务方基于视图表达可以定制化支持业务场景。
Map 计算模型
下面介绍下该引擎的逻辑动态性。我们使用的是简单的 map 模型。
数据进来后判断是否是需要的,过滤清洗之后需要的数据进入下游,不需要的数据就丢弃掉。基于 Groovy 语言的热加载,将语言转换成可执行的逻辑。
拓扑重构
字节跳动的业务新增速度很快,我们希望新增业务下游后也不需要重启。对于业务方来说,用户只关心业务逻辑,运维关心底层稳定性和 Job 执行效率。但是在实际处理中,一个大的困境在数据源。我们以 Kafka 为例,每多一个消费者就多一份网络消耗和数据反序列化的计算成本,对 Kafka 的压力就越大。我们应对的方法原理其实很简单,即基于源数据集来进行重构。
相同 SLA 下的业务线,只要用了相同的 source,就可以把拓扑重构为新的模型。拓扑重构之后,用户侧无感知,SLA 也没有打破,但是效率确实成倍提升,而且对于上游 Kafka 的压力小了许多。
实时动态处理引擎整体架构
我们希望这个引擎是一个会变形的引擎。上游用户可以通过 SQL、图形化/界面化配置,我们可以根据 schema 产生的 catalog 生成一个通用的自定义逻辑规则。之后用户还会对逻辑规则进行修改,比如进行校验或函数重构,我们再会转换成用户的物理规则(physical rule),我们现在是使用 Groovy 进行转换。转换成物理规则之后,还有其他一些问题要处理。
首先是动态化,包括:
UDF 动态化:我们期望 UDF 改变也不用重启,所以 UDF 需要进行动态化编译。
拓扑重构动态化:重构之后拓扑改变,需要新的拓扑结构。
RPC 动态化:可以加载动态的函数。
这些配置更新以后,经过 Planbuilder 生成 JobGraph,引擎再拉取配置。
这时也有一个问题:我们的规则非常多,不能因为一条规则的更改就更新所有规则。所以我们做的是增量更新,只对有需要的规则进行更新。
引擎拉取之后,会加载新的资源(RPC、Schema),并进行拓扑重构以及编译。因为之前给到的是一些 Groovy 的代码片段,用户可以将其热编译为物理规则。
此外我们还做了很多细致的工作,例如 Object catch。
举个例子:大部分埋点上报的是 String 格式的 Json 数据,用户在进行数据清洗时就需要将 String 反序列化为 Json object,如果用户在规则中多次用到该 Json object 就会导致多次反序列化计算。因此,我们将反序列化后的 object 进行缓存,这样再次使用时就可以直接使用,避免重复反序列化成本。
欢迎关注字节跳动数据平台同名公众号
版权声明: 本文为 InfoQ 作者【字节跳动数据平台团队】的原创文章。
原文链接:【http://xie.infoq.cn/article/c2943df70c790622fb61d7c00】。文章转载请联系作者。
评论