电商用户标签服务系统建设及演进

07 Nov 2018

随着电商平台的体量越来越大,用户个性化运营的诉求也越来越突出,用户标签系统,做为个性化千人千面运营的基础服务,应运而生。唯品会的用户标签系统,目前已全面服务于唯品会的各种个性化运营场景,包括站外广告定向人群投放,站内个性化运营玩法,用户触达个性化沟通,提供包括用户的基础人口属性标签,设备属性标签,用户兴趣标签,用户行为标签,以及用户的会员属性标签等,为公司的千人千面策略提供最基础的数据服务。

用户标签系统搭建的初期,主要还是依靠运营同学的人工经验基于规则圈人群运营,之后随着不断提升运营的效率,系统逐步转变成为运营赋能的用户标签数据平台,不仅仅提供用户标签数据,还包括用户画像分析服务,辅助业务运营进行人群洞察分析,以及通过算法能力,帮助业务方优化运营策略,实现策略的效果最大化。整个演进的过程围绕着业务的诉求不断升级,可分为三个阶段,本文将逐一进行介绍。

用户标签服务的0-1

其实在用户标签服务这个系统诞生之前,公司存在多个提供用户标签数据的系统,有些标签是由A系统提供,有些由B系统提供,同时根据数据最终提供的形式的不同,又分别存在以人群包为维度提供离线批量获取用户的系统以及将人群包标签化打到用户上,提供以用户为维度的在线标签查询的系统。因此,用户标签服务系统的搭建,一方面是整合管理散乱在各处的用户标签数据,另一方面也是对用户标签业务的统一管理。

系统建设的初期,业务诉求主要集中在基于已经存在的用户基础标签之上,根据业务经验总结出的各类规则进行圈人群,从而达到精准营销的目的。比如针对华南区的用户做一次美妆活动,业务要找出最近一个月有收藏过美妆品类商品或者最近一个月有浏览过美妆品类商品的华南区女性用户,对她们进行精准触达召回营销,同时在站内也会针对这群用户投入相应的广告位来吸引她们。

围绕业务的需求场景,用户标签系统初期的建设主要围绕:

系统整体架构如下:

Architecture Overview

用户基础标签清洗加工

根据不同的场景需求以及数据本身的时效性特点,目前基础标签的清洗加工分为离线和实时两套处理方式。离线处理主要通过Hive/Spark以天为单位进行数据清洗和加工;实时处理则通过Kafka接入业务实时数据源,再由Storm流式处理,实时清洗和加工。

业务圈人标签规则计算

基于用户的基础标签,业务方从运营的角度可筛选各类日常运营的人群标签,例如最近一周有浏览Nike运动鞋但没有购买的人,有这样行为的人,可认为是近期对Nike运动鞋非常感兴趣的人群,给他们打上标签后,便能针对这群人进行精准触达沟通和站内外的广告投放了。

标签规则计算大部分是通过离线(以天为单位)计算完成,借助一套标签规则的解析引擎,将标签规则转化为数据层面的计算逻辑,完成计算后得到该标签对应人群,同时系统提供一套灵活的基础标签配置管理,在基础标签数据开发完成后,通过配置便可快速完成该标签的上线工作。

为了保证标签数据的准确性,系统提供了一套简单有效的数据质量检查机制,例如通过对比圈选的人群最近几天的人数变化情况,对有人数突变的人群及时告警,再通过人工的介入进行二次排查,可及时发现至少80%的数据质量问题。同时,为了减低数据问题对业务的影响,系统保留了最近1-2天的数据版本,在当前数据版本出现问题时,可快速回退至之前的数据版本,降级提供前天(有损)的数据。

标签存储及查询

通过标签规则计算的标签结果,最终以用户ID List的形式存储在Hive表里,鉴于公司用户规模比较庞大,最终一个标签的用户数量往往有百万级,多的甚至达到千万级。为了提供稳定快速的人群取数服务,系统内部会先将标签结果拉取到Redis,最终查询接口通过Redis里的标签数据提供查询服务。

标签查询服务从业务使用的方式上可划分为两类:A)以标签为维度,将符合该标签的所有用户都取出来,业务使用方式以批量发送Push/短信为主;B)以用户为维度,查询该用户身上是否符合某个标签或多个标签,业务使用方式包括广告定向投放,站内个性化运营等。A是离线批量查询,B是在线实时查询,在标签存储上对这两类以不同的形式分开存储。

首先,将Hive表中的标签结果用户ID List数据同步到Redis中,也是以List的形式存储,为了避免产生Redis的大Key,以及提高数据同步的并行度,这里的List是逻辑上的,实际存储时则是以一段段的小List拼接而成,同时,为了节省Redis的存储空间,将多个User的ID拼接作为List的一个element:

User List Storage

另外,为了提供用户维度的标签查询,需要对用户打标签,在上面的生产的Redis List数据中,对拥有该标签的每个user进行遍历,以用户id为key,标签id为field,标签值(符合/不符合该标签)为value构造Redis的hash结构,一个hash存储一个用户身上的所有标签。对用户标签的查询,采用Redis针对hash结构的hmget操作即可获取某个用户的一个或多个标签的值。

上面说了标签存储和如何进行查询,那么标签数据的更新是怎样的呢?对于业务会经常使用的标签,数据是保持每天更新的,但往往变化的差异是比较小,因此为了提升用户打标签的速度,在同步标签结果时,系统会生成多一份该标签的用户id bitset,通过和昨天的数据bitset进行差集比较,可快速标识出每天变化的用户,进行增量打标,可大幅提高打标速度。

最后,在数据可靠性上,对极其重要的业务所用到的标签数据,数据的可靠性要求非常高,使用Redis作为存储的方案存在一定的风险,因此,针对这部分重要标签,同时会有一套MySQL的存储,当Redis出现问题时,可快速回源到MySQL进行标签的查询。在MySQL的表结构设计上,没有采用以用户id为主键的方式,主要是考虑到数据的更新是以标签为维度,对用户进行批量更新,而一个用户也可能同时存在多个标签中,以用户id为主键会导致数据的更新逻辑复杂,无法对用户批量更新,数据更新速度慢;因此,同时考虑到数据更新的问题和标签查询的性能,最终采用以标签id为主键,沿用上面bitset的思路,将该标签的用户id构成的逻辑上的一个大bitset,按照顺序等长拆分为多个bitset_segment:

User List Storage

假设bitset的总长度有5亿,就代表最多可以表示5亿用户,例如上图中,符合标签A的有u2,u871,u3188等,根据每个用户的序号,可以计算出每个用户所属的bit index,在标签A的bitset中对应位置index设为1,这是逻辑上的bitset,实际存储时会将bitset按顺序拆分成多个bitset segments,假设有50个segments,那么每个segment就1千万个bit,而每个用户所属的segment index以及segment中的bit index也是可以由用户的序号计算出来的,这样对用户标签的查询操作,更新操作,都转为标签id+segment_id进行操作。

用户标签服务2.0 – 用户画像分析引擎

在满足用户个性化标签运营需求后,为了帮助业务方高效的进行个性化人群标签的筛选,系统提供了一套用户画像分析引擎–天眼,借助天眼强大的分析能力,可实时地查看标签筛选人群结果的画像构成:包括人群数量规模;人群的基本用户信息,例如性别/年龄/地域/会员等级分布等;用户行为信息,例如购买品牌品类分布,访问频次,深度,时段等;以及人群维度的一些运营指标,例如最近UV,转化率等。这样业务方可快速调整筛选人群的条件规则,找出认为适合的人群。

这是2.0的系统整体架构图:

Architecture Overview 2.0

为了能实时并且准确的提供用户画像分析能力,系统采用Elasticsearch(ES)作为分析引擎,利用其快速的查询性能和数据聚合能力,可实现秒级完成各种复杂的条件组合和数据聚合运算。另外,由于用户的数据规模庞大,考虑到ES索引存储以及数据更新的压力,放入到ES的索引数据为抽样的用户数据,在满足数据准确性的同时,尽量降低ES的压力。

索引数据结构设计

一个用户对应一个doc,用户的标签信息是doc fields,简单的标签例如性别,年龄,直接使用普通的fields即可;一些复杂的标签信息,例如用户对品牌A的最近一个月浏览次数这类,属于用户和品牌一对多的动态标签信息,需要采用nested fields,一个简单的示例结构如下:

{
  //normal fields
  "basic_info": {
    "age": 20,
    "gender": "female"
  },
  //nested fields
  "brand_info": [
    {
      "brand_id": "123456",
      "brand_last_purchase_date": "2018-01-01",
      "brand_30d_visit_cnt": 10
    },
    {
      "brand_id": "67890",
      "brand_last_purchase_date": "2018-01-05",
      "brand_30d_visit_cnt": 16
    }
  ]
}

用户标签抽样表设计

采用id取模的方式对用户进行抽样,可确保每次抽样的用户基本固定。同时,抽样用户的标签信息由多张Hive表存储,例如基础信息表,品牌行为信息表,品类行为信息表等,有的是和用户成1:1 关系,有的是和用户成1:n关系的,例如品牌行为信息表。在处理这些表的数据时,也会做一些小优化,例如时间类型的统一采用日期形式,这样可以节省ES的存储空间。

索引数据更新机制

抽样的用户标签数据在Hive表中完成更新后,需要再同步到ES中,数据刷新的机制经历过多次的优化迭代,一开始通过Hive表中的数据为每个抽样用户构造完整的用户json数据,再通过一个Job将ES中的数据进行全量更新,更新过程中发现更新认为的Job应用cpu非常高,主要原因是每个用户的json数据特别大,而且很复杂,在将Hive的数据序列化成一个大json时CPU消耗非常大;后来逐步优化,将全量更新调整为用户级别增量更新,并且由原来的完整json改为仅对有变化的fields按Hive表拆分各个不同fields group构造json,分批部分更新,采用小步快跑的方式完成索引数据的更新。

用户标签服务3.0 – 智选标签

回顾系统在1.0和2.0两个阶段,都是围绕业务根据经验筛选人群,建立标签而建设的,而业务的最终诉求,是要找出符合某些运营场景的人群,并结合运营策略实现运营目标的最大化。例如前面例子中描述的“最近一周有浏览Nike运动鞋但没有购买的人”,背后的需求其实是找出“最近对Nike运动鞋有购买倾向的人”,放入到对应的广告/Push等运营活动中,达成活动的运营目标,比如转化率达到多少,至于如何找出最近有购买倾向的人呢?根据业务经验,认为“最近一周有浏览过但又没有购买的人”是一个潜在的购买客群,但并不一定是最精准的人群,在现在这样一个智能时代,运用数据的积累,算法的挖掘,借助机器学习建立模型,能找出更合适的人群,同时再结合运营策略,满足运营目标的最大化需求,这就是3.0的智选标签服务。

3.0的标签模型框架:

Architecture Overview 3.0