几乎所有的互联网行业均有精准的营销方面的需求,通过系统快速进行用户的圈选,然后针对于圈选出这一批用户进行精准的营销、广告等方面的投放,以求达到效果最佳。
但同时面临着用户基数大,标签多,对于人群圈选的时效性也高,圈选的条件组合多样等特点,因此人群圈选是画像标签中绕不过去的一个话题,本文主要从解决此问题提供一些解决思路。
一、基于MYSQL列存储方式
表结构
KEY: 用户ID
标签1:
标签2:
...
标签N:
实现方式:每个标签字段一个索引。
select .......
where 标签a and 标签b and ...
显然这种方式,有着较为明显的缺点。
1、用户的标签一多,那么字段也就多,所占用的存储空间也就多起来,并且很多列的数据均是空的,极大的浪费了存储空间。
2、MYSQL所支持的列数也是有限的,当达到MYSQL的最大列数之后,将无法进行扩展新的标签。
3、圈选人群的方式是依赖MYSQL的索引查询,每个字段均是一个索引,存储空间也会带来成倍的增加,还会影响到数据的更新。
4、当新增一个用户群体标签时,需要更新大量数据。
因此该方案只能用于一些用户基数和用户标签数较少的场景。
二、基于PostgreSQL数组存储方式
表结构
KEY:用户ID
VALUES:标签数组
实现方式:采用针对标签数组字段:建立倒排索引
SELECT ......
where VALUES @> array[标签s] -- 与
where VALUES && array[标签s] -- 或
where not VALUES @> array[标签s] -- 非
该方案采用了数组替代了多字段存储标签的方式,在一定程度解决了字段多,且存储空间被浪费的问题,但采用倒排索引这种方式去提升圈选人群的效率这种方式不可避免的依然存在空间暴增的问题,同时新增一个用户群体标签时,同样需要带来的大量数据更新的情况。
三、基于Bitmap存储方式
表结构
KEY:标签ID
VALUES: 用户bitmap
实现方式:针对于标签ID字段建立Btree索引
聚合bitmap: 与、或、非
and_agg(bitmaps) where KEY in (标签s) -- 与
or_agg(bitmaps) where KEY in (标签s) -- 或
except(bitmap1,bitmap2) -- 非
该方案在很大程度解决了表空间和索引空间占用大的问题,新增一个用户群体标签时,也无需进行大量数据更新,仅需新增一条人群的bitmap记录即可,查询性能也非常好,基本在毫秒类,但该方案也存在一些问题。
1、bitmap最大长度为1GB,用户数如果超过这个最大长度是,只能采用曲线救国的方式,通过offset去实现
2、bitmap是采用0,1占位来表示,不管存在与否,均需要占用空间,因此存在着浪费存储空间的问题
因此基于此方案基础上,存在一个改良的版本,使用Roaring BitMap压缩Bitmap对空间进行压缩,避免空间浪费。
Roaring Bitmap算法实现原理
Roaring Bitmap算法是将32位的INT类型数据划分为216个数据块(Chunk),每一个数据块对应整数的高16位,并使用一个容器(Container)来存放一个数值的低16位。Roaring Bitmap将这些容器保存在一个动态数组中,作为一级索引。容器使用两种不同的结构:数组容器(Array Container)和位图容器(Bitmap Container)。数组容器存放稀疏的数据,位图容器存放稠密的数据。如果一个容器里面的整数数量小于4096,就用数组容器来存储值。若大于4096,就用位图容器来存储值。
采用这种存储结构,Roaring Bitmap可以快速检索一个特定的值。在做位图计算(AND、OR、XOR)时,使得Roaring Bitmap无论在存储和计算性能上都表现非常优秀。
总结
第一种方案相对于比较简单,对存储的选择也没有什么特别的要求,基本上的存储都能支持(无论是关系型数据库还是分析性数据库均可),对于第二种使用数组作为标签存储,在存储层面还是有一些要求的,至少MYSQL是不支持这种玩法的,针对于第三种方案,可能更多的是一些分析性数据库才能支持,针对于Roaring Bitmap改良版可能只有一些云厂商的数据库才能支持,因此在技术选型的时候也要根据实际情况,具体问题具体分析。