导语 | We 分析是微信小程序官方推出的、面向小程序服务商的数据分析平台,其中画像洞察是一个非常重要的功能模块。微信开发工程师钟文波将描述 We 分析画像系统各模块是如何设计,在介绍基础标签模块之后,重点讲解用户分群模块设计。希望相关的技术实现思路,能够对你有所启发。
目录
1 背景介绍
1.1 画像系统简述
1.2 画像系统设计目标
2 画像系统整体概述
3 基础标签模块
3.1 功能描述
3.2 技术实现
4 用户分群模块
4.1 功能描述
4.2 人群包实时预估
4.3 人群创建
4.4 人群跟踪应用
5 总结
背景介绍
We 分析是小程序官方推出的、面向小程序服务商的数据分析平台,其中画像洞察是一个重要的功能模块。该功能将为使用者提供基础的画像标签分析能力,提供自定义的用户分群功能,从而满足更多个性化的分析需求及支撑更多的画像应用场景。
在此之前,原有 MP 的画像分析仅有基础画像,相当于只能分析小程序大盘固定周期的基础属性,而无法针对特定人群或自定义人群进行分析及应用。平台头部的使用者均希望平台提供完善的画像分析能力。除最基础的画像属性之外,也为使用者提供更丰富的标签及更灵活的用户分群应用能力。因此, We 分析在相关能力上计划进行优化。
整体来看,平台支持灵活的标签及人群创建方式,使用者按照自己的想法任意圈选出想要的人群,按不同周期手动或自动选出人群包。此外也支持人群的跟踪分析,人群在多场景的应用等。
画像系统整体概述
系统从产品形态的角度出发,在下文分成2个模块进行阐述——分别是基础标签模块及用户分群模块。
基础标签模块
该模块主要满足使用者对画像的基础分析需求,预期能满足绝大部分中长尾使用者对画像的使用深度要求。主要提供的是针对小程序大盘的基础标签分析,及针对特定人群(如活跃:1天活跃、7天活跃、30天活跃、180天活跃)的特定标签分析。如下所示:
从上述功能的描述,可以看出功能的特点是官方定义数据范围可控,支持的是针对特定人群的特定标签分析。
针对特定人群的特定标签分析数据是用离线 T + 1 的 hive 任务进行计算。流程如下。
分别计算官方特定标签的统计数据、特定人群的统计数据,以及计算特定人群交叉特定标签的数据。
当前整个平台的预计算数据出库到 TDSQL 的数据达到十亿级别,数据表超百张,实际使用存储上百 T 。TDSQL 整体功能较为全面,开发者仅需要补充开发数据生命周期管理工具,删除方式的注意点跟 MySQL 一样。
如果采用 KV 类型的引擎进行存储,需要根据 KV 的特性合理设计存储 Key 。在查询端对 Key 进行拼接组装,发送 BatchGet 请求进行查询。整个过程开发逻辑会相对繁复些, 需要更加注重 Key 的设计。若要实现一个只有概要数据的趋势图,那么存储的 Key 需要设计成类似格式:{日期} # {小程序} # {指标类型} 。
用户分群模块
该模块主要提供自定义的用户分群能力。用户分群依据用户的属性及行为特征将用户群体进行分类,以便使用者对其进行观察分析及应用。自定义的用户分群能够满足中头部客户的个性化分析运营需求,例如客户想看上次 618 参加了某活动的用户人群,在接下来的活跃交易趋势跟大盘的差异对比;或者客户想验证对比某些人群对优惠券的敏感程度、圈选人群后通过 AB 实验进行验证。上述类似的应用会非常多。
在功能设计上,平台需要做到数据丰富、规则灵活、查询快速,需要支持丰富的人群圈选数据,并且预置标签、人群标签、平台行为、自定义上报行为等。支持灵活的标签及人群创建方式,让客户能按照自己的想法任意圈选出想要的人群,按不同周期手动或自动选出人群包,支持人群的跟踪分析、人群在多场景的应用能力。
人群包实时预估是根据使用者客户定义的规则,计算出当前规则下有多少用户命中了该规则。产品交互通常如下所示:
为了满足客户能随意根据自己的想法圈出想要的人群,平台支持丰富的数据源。整体画像的数据量较大,其中预置的标签画像在离线 HDFS 上的竖表存储达近万亿/天,平台行为百亿级/天,且维度细,自定义上报行为百亿级/天。
怎么设计能节省存储同时加速查询是重点考虑的问题之一。大体的思路是:对预置标签画像转成 Bitmap 进行压缩存储,对平台行为明细进行预聚合及对维度枚举值进行 ID 自增编码,字符串转成数据整型节省存储空间。同时在产品层面增加启用按钮,开通后导入近期数据,从而控制存储消耗,具体细节如下。
例如活跃标签 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' )
)
如果把标签理解成对用户的分群,那么符合某个标签的某个取值的所有用户 ID(UInt类型) 就构成了一个个的人群。Bitmap 是用于存储标签-用户的映射关系的、非常理想的数据结构,最终需要的是构建出每个标签的每个取值所对应的 Bitmap。例如性别这个标签组,背后对应的是男性用户群和女性用户群。
性别标签:男 -> 男性用户人群包,女 →女性用户人群包。
同时会对维度枚举值进行 ID 自增编码,目的是减少存储占用,写入以及读取性能;从效果来看,团队对可枚举类型进行字典 ID 编码对比原本字符类型能节省60%的线上存储空间,同时相同数据量条件下带来 2 倍查询速度提升。
综合上述调研,团队采用 ClickHouse 作为画像数据存储引擎。在 ClickHouse 中使用 RoaringBitmap 作为 Bitmap 的解决方案。该方案支持丰富的 Bitmap 操作函数,可以十分灵活方便的判重和进行基数统计操作,如下所示。
采用 RoaringBitmap(RBM) 对稀疏位图进行压缩,可以减少内存占用并提高效率。该方案的核心思路是,将 32 位无符号整数按照高 16 位分桶,即最多可能有 216=65536 个桶,称为 container。存储数据时,按照数据的高 16 位找到 container (找不到则会新建一个),再将低 16 位放入 container 中。也就是说,一个 RBM 就是很多 container 的集合,具体参考高效压缩位图 RoaringBitmap 的原理与应用。
具体步骤是 Spark 任务首先会按照 id 进行分片处理,然后对每个分片中标签的每个标签值生成一个 Bitmap ,保证定制的序列化方式与 ClickHouse 中的 RBM 兼容。其中通过 Spark 处理后的 Bitmap 转成 string 类型,然后写入到线上的标签表中,在表中业务团队定义了一个物化列字段,用于实际存储 Bitmap。在写入过程中会将序列化后的 Bitmap 字符串通过 base64Decode 函数转成 ClickHouse 中的 AggregateFunction (groupBitmap, UInt32) 数据结构。
具体表结构如下:
CREATE TABLE xxxxx_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
对于查询性能的保障,团队始终保证所有查询均在本地表完成。上面已经介绍到数据在入库时,均会按照相同用户 ID 的 hash 分桶规则出库到相应的机器节点中。使用维度数字编码,测试数字编码后对比字符方式查询性能有2倍以上提升。对标签对应的人群转成 Bitmap 方式处理,用户的不同规则到最后都会转成针 Bitmap 的交并差补集操作。
对于平台行为,如果在用户用模糊匹配的情况下,会先查询维度 ID 映射表,将用户可见维度转化成维度编码 ID,后通过编码 ID 及规则构建查询 SQL。整个查询的核心逻辑是根据圈选规则组合不同查询语句,然后将不同子查询通过规则组合器最终判断该用户是否命中人群规则。
基于rpc开发服务接口:查询的服务接口采用 rpc 框架进行开发。
在数据服务的上一层是团队的数据中间件,统一做了流量控制、异步调用、调用监控及参数安全校验,特别是针对用户量较大的 app 在多规则查询时,耗时较大,因此业务团队配置了细粒度的流量控制,保障查询请求的有序及服务的稳定可用。
从性能数据看,对用户量大的 app 来说,在规则非常多的情况下还是要大几十秒,等待这么长时间体验不佳。因此对于这部分用户量大的 app,业务团队采用的策略是抽样。通过抽样,速度能得到非常大的提升,并且预估的准确率误差不大,在可接受的范围内。
人群包实时创建类似上面描述的人群大小实时预估,区别是在最后人群创建是需要将圈选的人群包用户明细写入到存储中,然后返回人群包的大小给到用户。同样是在本地表执行,生成的人群包写入到同一台机器中,保持分桶规则的一致。
客户创建的例行化人群包,需要每天计算。如何持续跟踪分析趋势,并且不会对集群造成过大的计算压力?团队的做法利用离线超大规模计算的能力,在凌晨启动所有人群计算任务,从而减小对线上 ClickHouse 集群的计算压力。所有小程序客户创建的例行化人群包计算集中到凌晨的一个任务中进行,做到读一次数据,计算完成所有人群包,最大限度节省计算资源,详细的设计如下:
首先,团队会先将全量的数据(标签属性数据+行为数据)按照小程序粒度及选择的时间范围进行过滤,保留有效的数据;
其次,对数据进行预聚合处理,将用户在一段时间范围的行为数据,标签属性镜像数据按照小程序的用户粒度进行聚合处理,最终的数据将会是对于每个小程序的一个用户仅会有一行数据;那么人群包计算,实际上就是看这个用户在某个时间范围内所产生的行为、标签属性特征是否满足客户定义的人群包规则;
最后,对数据按用户粒度聚合后进行复杂的规则匹配,核心是拿到一个用户某段时间的行为及人群标签属性,判断这个用户满足了用户定义的哪几个人群包规则,满足则属于该人群包的用户。
在按照用户规则圈选出人群后,统一对人群进行常用指标(如活跃、交易等指标)的跟踪。整个过程用离线任务进行处理,会从在线存储中导出实时生成的人群包,以及离线批量生成的定时人群包,汇总一起,后关联对应指标表,输出到线上 OLTP 存储进行在线的查询分析。其中,导出在线人群包会在凌晨空闲时间进行,通过将人群 RBM 转成用户明细 ID。
具体方法为:arrayJoin(bitmapToArray(groupBitmapMergeState(rbm)))。
人群基础分析对一个自定义的用户分群进行基础标签的分析,如该人群的省份、城市、交易等标签分布。人群行为分析,分析该人群不同的事件行为等。
在 AB 实验中的人群实验,使用者通过规则圈选出指定人群作为实验组(如想验证某地区的符合某条件的人群是否更喜欢参与该活动),跟对照组做相应指标的对比,以便验证假设。
总结
本篇回顾了 We 画像分析系统各模块的设计思路。在基础模块中,业务团队根据功能特性,选用了站长素材网 TDSQL 作为在线数据的存储引擎,将所有预计算数据都使用 TDSQL 进行存储。在人群分析模块中,为了实现灵活的人群创建、分析及应用,业务团队使用 ClickHouse 作为画像数据的存储引擎,根据该存储的特性进行上层服务的开发,以达到最优的性能。
后续,小程序 We 画像分析系统在产品能力上会持续丰富功能及体验,同时扩展更多的应用场景。以上是 We 画像分析系统模块设计与实现思路的全部内容,欢迎感兴趣的读者在评论区交流。
-End-
原创作者|钟文波
技术责编|钟文波、谢慧志
你可能感兴趣的腾讯工程师作品
| ChatGPT深度解析:GPT家族进化史
| 腾讯工程师聊 ChatGPT 技术「文集」
| 微信全文搜索耗时降94%?我们用了这种方案
| 10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈
技术盲盒:前端|后端|AI与算法|运维|工程师文化
后台回复“小程序”,领本文作者推荐的更多资料