微信小程序是如何设计百亿级用户画像分析系统的?
导语 | “We分析”是微信小程序官方推出的数据分析平台,其中画像洞察是其中一个非常重要的功能模块。微信开发工程师钟文波将描述We分析画像系统各模块是如何设计,在介绍基础标签模块之后,重点讲解用户分群模块设计。希望相关的技术实现思路,能够对你有所启发。
1)画像系统简述
“We分析”是小程序官方推出的数据分析平台,其中画像洞察是其中一个重要的功能模块。该功能将为用户提供基础的画像标签分析能力,同时提供自定义的用户分群功能,以满足更多个性化的分析需求及支撑更多的画像应用场景。2)画像系统设计目标
支持灵活的标签及人群创建方式,用户按照自己的想法任意圈选出想要的人群,按不同周期手动或自动选出人群包。支持人群的跟踪分析,人群在多场景的应用等。
画像系统整体概述
1)多源数据
2)画像加工 主要是对用户属性、人群标签、平台行为进行相应的ETL及预计算。
3)人群计算 根据用户定义的用户分群规则,从多源数据中计算出对应的人群。
4)画像导入
5)画像服务 提供在线的画像服务接口,其中标签管理使用通用配置系统,数据服务rpc框架用的是svrkit-javamesh,在上一层是我们的数据中间件,统一做了流量控制、异步调用、调用监控、及参数安全校验。
6)画像应用 提供基础标签分析及针对特定人群的标签分析,及人群圈选跟踪分析及线上应用等。
基础标签模块
1)功能描述
2)技术实现
-
数据计算
- 数据存储
不同存储对比存在差异。 通过上述分析,我们这里需要存储的是预计算好的结果数据,同时业务的特点是按照小程序appid粒度进行多个数据主题统计的存储,第一直觉是适合用分布式OLTP存储;同时也对比了不同 的数据库,在选型过程中,主要考虑的点包括: 数据的写入,读取性能进行对比。
读取包括查询性能,读取接口是否简单灵活,开发是否简单;以及相关运维配套设施是否完善,如监控告警、扩容、权限、辅助优化等。
TDSQL有优势。 通过对上述存储引擎的对比,We分析平台基本所有的预计算结果数据,最终选用TDSQL来存储离线预计算结果数据,关于TDSql的几个关键点如下:
如果采用KV类型的引擎进行存储,需要根据KV的特性合理设计存储Key,同时在查询端,对key进行拼接组装,发送BatchGet请求进行查询,整个过程开发逻辑会相对多些,需要更加注重Key的设计。例如,实现一个只有概要数据的趋势图,那么我们存储的Key需要设计成类似格式:{日期}#{小程序appid}#{指标类型}。
1)功能描述
2)人群包实时预估
人群包实时预估是根据用户定义的规则,计算出当前规则下有多少用户命中了该规则,产品交互通常如下所示。-
数据加工
属性标签数据 :通常建设用户画像的核心工作就是给用户打标签,标签是人为规定的高度精炼的特征标识,如性别、年龄、地域、兴趣,也可以是用户的一些行为集合。这些标签集合抽象出一个用户的信息全貌,每个标签分别描述该用户的一个维度,各标签维度间相互联系,构成对用户的整体描述。 当前的用户属性及人群标签是由平台方提供,由平台每天进行统一的加工处理生成官方标签,平台暂时没有支持用户自定义的标签,因此这里主要说明平台标签是如何计算加工管理。
第一,标签编码管理。
例如活跃标签 10002,对标签的每个标签值进行编码如下:
对特定人群进行编码,基准人群是作为必选的过滤条件,用于限定用户的范围:
采用大宽表方式的存储,比如Elasticsearch和公司的Hermes存储,需要等待全部需要线上用到的画像标签在离线计算环节加工完成才能开始入库。而像Clickhouse、Doris则可以采用与竖表相对应的表结构,标签加工完成就可以马上出库到线上集群,从而减小因为一个标签的延时而导致整体延时的风险。
CREATE TABLE table_xxx(ds BIGINT COMMENT '数据日期',label_name STRING COMMENT '标签名称',label_id BIGINT COMMENT '标签id',appid STRING COMMENT '小程序appid',useruin BIGINT COMMENT 'useruin',tag_name STRING COMMENT 'tag名称',tag_id BIGINT COMMENT 'tag id',tag_value BIGINT COMMENT 'tag权重值')PARTITION BY LIST( ds )SUBPARTITION BY LIST( label_name )(SUBPARTITION sp_xxx VALUES IN ( 'xxx' ),SUBPARTITION sp_xxxx VALUES IN ( 'xxxx' ))
性别标签:男 -> 男性用户人群包,女 →女性用户人群包。
平台行为数据: 平台行为指官方进行上报的行为数据,例如访问、分享、交易等行为数据,商户不需要进行任何埋点等操作。我们主要是会对平台行为进行预聚合,计算同一维度下的PV数据,已减少后续数据的存储 及计算量。
同时会对维度枚举值进行ID自增编码,目的是减少存储占用,写入以及读取性能;从效果来看我们对可枚举类型进行字典ID编码对比原本字符类型能节省60%的线上存储空间,同时相同数据量条件下带来2倍查询速度提升。
自定义上报数据: 自定义上报数据是商户自己埋点进行数据的上报,上报的内容包括公共参数及自定义内容,其中自定义内容是key-value的格式,在OLAP引擎中我们会将用户自定义的内容转成map结构类型进行存储。
-
数据写入存储
首先讲下,在线OLAP存储选型。 标签及行为明细数据的存储引擎选型对于画像系统至关重要,不同的存储引擎决定了系统不同的设计方式;我们调研了解到,我们公司内外在建设画像系统上,有多种不通过的存储方案。我们对常用的画像OLAP引擎做了对比,如下:
数据导入线上存储 :在确定了采用什么存储引擎存储线上数据后,我们需要将离线集群的数据导入到线上存储,其中对于标签数据通常的做法是将原始明细的id数据直接导入到ClickHouse表中,再通过创建物化视图的方式构建RBM结构进行使用。 问题是我们的明细数据非常大每天有5000亿+,这样的导入方式给Clickhouse集群带来了很大资源开销 。而通常我们处理大规模数据都是用Spark这样的离线计算框架来完成处理。最后我们也是把预处理工作全部交给了Spark框架,这种方式大大的减少了写入的数据量,同时也减少了Clickhosue集群的处理压力。
具体步骤是Spark任务首先会按照useruin进行分片处理,然后对每个分片中标签的每个标签值生成一个Bitmap,保证定制的序列化方式与ClickHouse中的RBM兼容。其中通过Spark处理后的bitmap转成string类型,然后写入到线上的标签表中,在表中我们定义了一个物化列字段,用于实际存储bitmap,在写入过程中会将序列化后的bitmap字符串通过base64Decode函数转成Clickhouse中的AggregateFunction(groupBitmap, UInt32)数据结构,具体表结构如下:
CREATE TABLE wxg_mmbiz_dw.xxx_table_local on CLUSTER xxx(`ds` UInt32,`appid` String,`label_group_id` UInt64,`label_id` UInt64,`bucket_num` UInt32,`base64rbm` String,`rbm` AggregateFunction(groupBitmap, UInt32) MATERIALIZED base64Decode(base64rbm))ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/xxx_table_local', '{replica}')PARTITION BY toYYYYMMDD(toDateTime(ds))ORDER BY (appid, label_group_id, label_id)TTL toDate(ds) + toIntervalDay(5)SETTINGS index_granularity = 16
具体实现参考:SparkSQL & ClickHouse RoaringBitmap使用实践 ,我们主要是在此基础上,增加了分桶写入的功能。
存储占用问题: 标签类型数据用bitmap类型存储后,在集群一个分片占用的存储850G,8分片*双副本总计占用存储14T;平台行为当前累计40亿,集群单分片占用存储32G,8分片*双副本总计占 用存储512G,预计随着商户的使用的增多,预估数据增长20倍左右占用10T存储。
-
数据查询
数据查询方式: 人群圈选过程中,如何保障大的APP查询,在复杂规则情况下的查询速度,我们在导入过程中对预置画像+平台行为+自定义上报行为均按相同分桶规则导入集群,保证一个用户仅会在同一台机器,查询时始终进行本地表查询,避免进行分布式表查询。
对于查询性能的保障,我们始终保证所有查询均在本地表完成,上面已经介绍到数据在入库时,均会按照相同用户ID的hash分桶规则出库到相应的机器节点中。另外使用维度数字编码,测试数字编码后对比字符方式查询性能有2倍以上提升。对标签对应的人群转成Bitmap方式处理,用户的不同规则到最后都会转成针对bitmap的交并差补集操作。
基于Svrkit-javamesh开发服务接口: Svrkit-javamesh是我们团队开源的高性能RPC框架,目前已有多个部门在使用,可以理解成类似trpc-java的框架,是用java来开发rpc服务。
在数据服务的上一层是我们的数据中间件,统一做了流量控制、异步调用、调用监控、及参数安全校验,特别是针对用户量较大的APP在多规则查询时,耗时较大,因此我们配置了细粒度的流量控制,保障查询请求的有序及服务的稳定可用。
查询性能数据: 第一,不同DAU等级小程序查询性能。
第二,不同DAU等级小程序查询并发。
3)人群创建
-
人群实时创建
-
人群例行化创建
首先,我们会先将全量的数据(标签属性数据+行为数据)按照小程序appid粒度及选择的时间范围进行过滤,保留有效的数据;
其次,对数据进行预聚合处理,将用户在一段时间范围的行为数据,标签属性镜像数据按照小程序的用户粒度进行聚合处理,最终的数据将会是对于每个小程序的一个用户仅会有一行数据;那么人群包计算,实际上就是看这个用户在某个时间范围内所产生的行为及其标签属性特征是否满足商户定义的人群包规则;
最后,对数据按用户粒度聚合后进行复杂的规则匹配,核心是拿到一个用户某段时间的行为及人群标签属性,判断这个用户满足了商户定义的哪几个人群包规则,满足则属于该人群包的用户。
4)人群跟踪应用
-
人群跟踪分析
-
人群基础分析
-
实验人群定向
你可能感兴趣的腾讯工程师作品