为解决跨越式增长障碍,“增长王者”都做对了些啥?
11 月 24 日,在 TGO 鲲鹏会上海分会《技术领导者的增长黑客落地实践》活动上,神策数据联合创始人兼 CTO,TGO 鲲鹏会北京分会会员曹犟为大家带来了主题为《 一个用户行为分析产品的设计与实现》的演讲。以下根据当天演讲整理,有部分不改变原意的删减。
今天分享的主要内容是,当客户需要有一个产品做数据驱动、做增长黑客的时候,如何抽象理解客户需求,设计产品功能。明确了产品功能之后,怎样做技术选型,反过来再考虑因为这个技术选型会有哪些限制。
我们当时面临的第一个重要需求,是私有化云的纯 Saas 产品。所以我们觉得当时在市场上存在私有化部署需求。其中有几个重要的支撑点,第一个是数据的资产积累,比如朋友圈;第二个是数据的深度应用与二次开发;第三个是很大一部分客户普遍会存在合规的需求,比如金融、银行、券商。
第二个需求产生于具体的使用层面。市场流行的通用分析工具,都是一些非常固定的报表。但是我们意识到,不管是国外还是国内,都在尝试做一些更复杂、更精细化的分析性产品。包括我们自己也很想知道用户使用产品的活跃情况、层次分布,每天有多少人打开,每天打开几次,每天用多长时间等。
第三个需求是,当时很多公司的产品不仅仅是在 PC、APP 或小程序上,而是各个端都有。他们希望能够采集到一个用户在各个端里的所有信息,需要把各个端打通。同时还希望埋点越方便越好,甚至一行代码都不用写,非技术人员都可以做。另外,埋点不能影响性能。
第四个需求是时效性,传统的统计分析工具都是 T+1 的,而早期我们花了很大的代价,把时效性做到了秒级。现在不管对接几百个渠道去做广告投放,渠道本身都能够让你很实时甚至自动化地调整投放策略。如果广告效果的分析是 T+1 的,就很有可能损失一天的广告投放。
综合考虑这些需求,我们最终产品定位是:
一个可以私有化部署的用户行为分析产品;
它支持全端埋点,实时采集,一对多 ID-Mapping,可跨屏打通;
它能够进行精细化多维分析,可灵活定义维度指标;
具有漏斗、留存、回访、分群、归因、间隔等多种分析模型;
能够做到亿级 DAU,秒级入库,秒级查询。
通过以上需求,我们倒推出一些核心的技术决策。
既然我们要解决数据资产积累的问题,就要做私有化部署,在本质上解决运维问题。
我们当时没有选一些商业方案,而是以开源方案为主,除了考虑商业方面的问题之外,主要也是考虑到想让这些数据能够得到充分的复用,而这就得以开源化为主了。比如,所有的人都用 Kafka,你就应该用 Kafka,这是个顺理成章的问题。
所谓的 ETL 框架其实就是能够让多个产品线的几百份日志映射成一张表,现在如果还这样做,时效性将无法得到满足。
既然要做到亿级 DAU,那么在这样的数据量级上就要能够满足性能要求。所以我们选择了明细数据,而不是做预聚合,一是考虑时效性,二是分析的灵活性,三是为了在任意纬度指标都能够下钻筛选。
虽然产品设计取决于需求,但也不能都由需求倒推,客户要什么就做什么,还需要有自己的产品理念。我们的理念是要让数据的采集、建模、分析、反馈形成一个完整闭环,能够不停转下去。所谓的技术驱动,就是持续关注决策反馈的最终效果,不断调整决策、不停复盘分析。有了这个产品理念,就能更好地实现产品化及其商业价值,决定做什么,不做什么。
上图是我们的整体逻辑架构设计,最下面是对采集数据的抽象和整理。我们把所有的用户行为相关的数据,抽象成了行为、用户、项目这三个简单的量,这是第一层抽象。第二层抽象了常用的分析模式。有了这两层抽象,不管是产品、运营还是市场管理者,都可以通过分析模型访问相应的数据来解决常见的需求。
在我们的技术架构中,提供了一系列数据采集工具,其实仅在埋点或者客户端采集上,就有很多需要解决的问题。客户会希望数据采集不影响用户体验,这就需要有一个非常合适的数据发送策略,以保证用户体验为核心。最简单的做法就是不要同步发送数据,而是首先缓存到本地,有一定规模的时候才发送。应该在网络比较好的时候发,而不是在连个文章都刷不出来的时候,还不停地发数据。
SDK 除了采集各种各样基础的属性数据,还要解决发送过程中造成的重复、缺失的问题,然后再向上提供给 API。这些 API 如果通过代码显式调用,叫代码埋点,如果是在编译过程中自动插代码能自动采集,叫全埋点。
除了要解决不同数据源的采集问题,还要要解决跨屏贯通的问题,我们叫做 ID-Mapping。本质上可以把 ID 分为两大类:一类是设备 ID,比如小程序的 ID、安卓的 ID、网页上的 Cookie 等;另一类 ID 是手机号、微信号,或系统内的注册 ID 等唯一的用户标识 ID。在目前已有的技术条件下,我们倾向于实现一个注册 ID 和多个匿名 ID 的关联。这其实是多对多的关系,指一个数据上可能有多个自然人使用,也可能是一个真实自然人通过多个设备来使用。可以简单理解为有一张表单独记录一个注册 ID 和多个匿名 ID 之间的关系。
我们认为采集到的数据都是统一的数据模型,通过统一的 API 发到数据接入子系统里。这个子系统比较简单,对公网的数据都提供 API,比如在 IOS 上异步存了五十条数据,打包 post 给 Nginx,Nginx 将其落盘成一条日志,Extractor 模块可以实时读取和处理这些日志,并将处理结果发布到 Kafka。在这个过程中,我们能把 IP 替换成不同的地域、解读出机型、记录 ID-Mapping 之间的关系。其整体性能非常高,能够同时满足非常高的导入用户量,这就降低了对机器的需求量,若要节省成本,后面的数据处理能力只做两倍的冗余就可以了。
之所以采用 Kafka,而不是把数据写到后面的存储,是出于一些很简单的考虑。第一,接收和数据处理之间的性能存在差异,所以需要有消息队列作为中间的缓冲。第二,我们想把这些数据提供给产品使用者直接使用,如果需要可以直接订阅。第三,后面的数据处理可能会有 BUG,需要做数据回溯,这时没必要从日志重新采集数据,因为 Kafka 能起到了一个短期数据备份的作用。
数据到了 Kafka 之后,整个系统最关键性的挑战就是解决用户行为问题了,我们应该让它有足够的灵活性,整个数据模型应该足够简洁。整个的数据其实可以理解为三张表,Event、User 和 Item。Event 表跟 PV 基本是一个量级,今天如果有一百亿 PV,那么 Event 表里面就是一百亿数据,每一条 Event 数据都描述了这个人在什么时间、以什么方式做了件什么事。User 和 Item 相比 Event 的量级要少得多。
针对后面的分析,我们选择了两个存储引擎:Kudu 和 Parquet。引入 Kudu 做 WOS(Write Optimized Store),现在新的存储架构下,我们会实时从 Kafka 里订阅数据,写入到 Kudu 里。我们可以认为数据一旦写到 Kudu,就可以查到,这保证了整个数据导入的时效性。从 Nginx 接收到数据再到写入 Kudu,基本上可以保证在几十秒之内完成。而为了尽量压榨性能,让硬件能够充分发挥作用,我们仍会把数据存在本地一份,引入 Parquet 来做 ROS(Read Optimized Store),同时自己对数据列选择做精细的压缩方案,针对分区、索引做了大量的工作。
但这样就带来了读、写同步的问题,我们需要保证查询结果的正确性。这是个很复杂的处理方式,比如我们可以认为先写到 Kudu 的数据是写在第一张表里面,当达到阈值且不再写入时,将它转成多个 Parquet 文件,移动到对应的 Partition 中。在合适的时候,再把第一张表 drop 掉。
现在我们的数据有热数据和冷数据,热数据存在 Kudu,冷数据存在 HDFS。基于性能考虑,我们选择了 Impala,而非 SparkSQL,并针对在这个场景做了非常多的查询优化:
第一,用 Impala 查询两份不同存储上的数据是有成本的,就做一些具体的系统实现,以求零成本;
第二,在分析需求时,我们有非常多持续性的分析,比如漏斗。这就需要 Impala 能够支持 Union 这种聚合函数,所以我们又改了 Impala 的代码,让它能够支持这种聚合函数,以替代 Join;
第三,我们做了非常多的 Partition 和用户分桶,来减少数据扫描量;
第四,我们做了大小表 join 的优化;
第五,提前对数据做预查询缓存起来,只需要重查有变化的数据。
客户的硬件设施和管理系统五花八门,得保证你的系统在各种机器上能够部署,能够正常运行,性能符合最初的预期。这是前期决策带来的问题,如果只做一个 Saas 产品,运维会很简单,只需要维护这一个机群。
为了解决这个问题,我们花了非常多的精力。从授权阶段开始,我们就要跟客户说明最低的硬件要求,并用相应的检测工具检测是否满足要求。同时我们一个专门的运维系统,能够在客户允许的情况下,采集每一个模块在每台机器上的运行状态,统一汇总并且自动找到需要人工介入的地方,给运维发报警。同时把运维操作记录下来,从而可以有针对性地查看之前哪些报警发的多,需要做处理。
曹犟:这就是一种抽象,因为漏斗本质上是可抽象的,描述狭义上的漏斗是指同一个人按照先后的顺序在一个时间窗口之内所做的事。我们的解决方案,是在界面上可视化创建漏斗,可以设置这些事是什么。它需要在哪些纬度上做筛选,是否需要做分组,甚至需要加上关联 ID 的限制,创建好之后就可以去查了。那么漏斗的描述就变成一条 SQL,而因为我们没有做任何的预聚合,就没有丢掉任何的信息,在不考虑性能的情况下可以扫描所有的数据,按照 SQL 方式来查,没有必要再去开发一个后台。
曹犟:以安卓为例,其实是在编译的过程中插入埋点。但是我们会觉得这相当于变成所有的点都采,会采下来很多。有一种折中的办法是可以做一个配置,配置远程下发的方式,有些数据采了之后不发,或者接收了不处理。其实,本质上的确是在编译的时候插代码。
曹犟:这其实是两种数据应用的思路。第一种思路是先提出假设,例如我认为产品的核心转化是 ABCDE,可以看用户有没有很好地完成。第二种思路是,通过漏斗知道了转化率,知道哪些人转化了,哪些人没转化,就可以把这些人打一些标签,用很简单的算法看到转化用户和流失用户,他们之前在一些关键性的行为路径上的特征有什么大的差异,从而得到一些启发性的信息。所以这其实是两类数据应用的思路,第一类把数据变成了验证我问题的工具,第二类是启发性的,只知道存在问题,想知道到底是什么问题。
点击下方图片即可阅读
阿里王坚受邀成为 TGO 鲲鹏会荣誉导师
你想与神策数据联合创始人兼 CTO & TGO 鲲鹏会会员曹犟一起学习交流吗?