Clickhouse+Bitmap实现用户画像中海量用户的圈选
我们知道用户画像最主要的功能就是实现分客营销分客分析的功能,我们也介绍过可以使用画像宽表可以便捷的创建想要的客群,具体实现思路将 就是将Hive 表(大数据平台开发的标签宽表)中的数据同步到 ClickHouse 中进行缓存,人群圈选引擎可以从 ClickHouse 中直接查询到满足条件的 UserId然乎进行后续的业务开展。
比如江苏省中年男性用户,所有的标签拼接到一张数据表中并构建出一张宽表,上述圈选SQL语句如下语句。
SELECT user_id FROM dm_demo.userprofile_wide_table WHERE sex = '男' AND province = '江苏省';
我们可以看出,使用了画像宽表圈人的逻辑其实就是从明细数据中找到满足条件的用户并最终构建人群,也就是我们筛了多少标签就在对应的表后就对应的条件,如果海量标签的话,这个也会存在一定效率低下,这里我们可以引进bitmap的实现方式来优化,使用BitMap进行圈人的话,会对用户进行预聚合,在人群圈选时直接使用聚合后的结果进行计算。具体做法是首先将指定标签值下的所有用户聚合后生成BitMap,然后在基于这些做好的BitMap执行交集、并集、差集等操作来实现人群筛选。从而解决圈选速度的问题,来大大提升圈客的效率。下图是实现具体的bitmap包的图例。
bitmap(位图)是一种利用比特位来进行数据存储的结构,BitMap特殊的数据结构决定了它比较适合做用户聚合并应用到人群圈选场景下。具体实现是BitMap底层构建了一个bit数组,bit每一位只能存储1或者0,其中数组的索引值映射到UserId,当前索引上的数字是1的时候代表对应的UserId存在,是0的时候代表UserId不存在。图5-9展示了BitMap存储UserId的基本逻辑,UserId不再是一个具体数字而是映射到位数组的索引值上面,借助这一特点可以实现大量UserId数字的压缩、去重、排序和判存。
这个热时候,将大量的用户Id写入BitMap时,因为相同的UserId所对应的索引位置一样,可以自动实现人群用户Id的去重;bit数组索引是天然有序的,用户Id写入BitMap可以实现便捷排序;判存是判断UserId是否在人群中,通过判断bit数组指定索引位置的数值是否为1便可以快速判断出UserId是否存在。BitMap以上特点都非常适合存储人群数据,也决定了其在画像平台的广泛使用。
我们可以在clickhouse里转化为bitmap,但是有点时候也需要在hive 存一份备份或者而其他使用,所以我们可以在hive里直接转化好,Hive表数据转为BitMap依赖开源工具包hive-bitmap-udf.jar,这是个外包,需要导入,其中UDF函数to_bitmap可以将UserId列表转换为BitMap存储到Hive表中。工具包中还包含常用的UDF函数:bitmap_count、bitmap_and和bitmap_or等,可以便捷地对BitMap进行各类操作。Hive表中的BitMap数据经由Spark等大数据引擎批量处理后写入ClickHouse表中。
add jar hdfs://userprofile-master:9000/hive-bitmap-udf.jar;
create temporary function to_bitmap as 'com.hive.bitmap.udf.tobitmapudaf';
insert overwrite table userprofile_demo.gender_label_bitmap
partition(p_date = '2022-08-01')
select
sex
,to_bitmap(user_id)
from
dm_demo.gender_label
where
p_date = '2022-08-01'
group by sex;
bitmap数据存储,CK本身并没有这种类型,而是通过参数化的数据类型AggregateFunction(name, types_of_arguments…)来实现,建表示例:
CREATE TABLE userprofile_demo.tag(
`code` String,
`values` String,
`offset_bitmap` AggregateFunction(groupBitmap, UInt64)
)
ENGINE = ReplicatedMergeTree
('/clickhouse/tables/cdp/{shard}/group_1',
'{replica}'
)
PARTITION BY(code)
ORDER BY(code)
;
这样,源数据和加工后的bitmap都可以存储在CK中。
虽然我们用bitmap来优化圈客,但是并不是所有的画像标签都适合转换为BitMap,只有标签值可枚举的,并且数量有限的标签才适合转换为BitMap来支持人群圈选。例如:性别标签有男、女、未知三个标签值,标签值之间有明显的区分度,生成BitMap之后每个BitMap被使用的概率也较高,其比较适合构建标签BitMap。但是对于平均在线时长、关注粉丝数等数值型的标签,标签值是不可枚举的或者数量太过庞大,标签值之间没有明显的区分度,此类标签不适合构建BitMap。生成BitMap会消耗大量的计算和存储资源,如果标签值区分度较小,生成的BitMap数据被使用到的概率较低,是对计算和存储资源的浪费。这个时候我们就不会创建bitmap,而还是宽表。
使用画像宽表还是BitMap要根据业务特点来决定。基于宽表中全量用户的明细数据可以实现所有的人群圈选功能,但是采用BitMap方案的人群创建速度相比宽表模式可以提升50%以上。BitMap适用的标签类型和业务场景有限,要结合实际的数据进行判断。业界大厂一般使用混合模式,优先通过BitMap进行人群创建,不适用的场景下兜底使用画像宽表进行人群圈选。采用混合模式要考虑对齐画像宽表和BitMap的标签时间,这增加了工程的实现复杂度。
在实际场景中,可很时候都会会面临混合数据圈存的情况,既有宽表的明细数据,又有 BitMap 数据。所以在这种情况下,我们的关注点还是主要在于规则圈选引擎的优化。如果 我们创建的BitMap 能够覆盖大部分标签且计算速度比较理想,可以优先考虑使用 BitMap,也就是先先计算 BitMap 的部分,计算完成以后在与明细一起计算,最终自底向上汇总为执行结果。
总结,客群的圈选的方法是基于clickhouse实现,在某些特定的场景里引入 BitMap,采取混合圈客的方法,并在后期复杂模式下对圈选规则引擎进行多方便的优化,来全面提高客群筛选的效率。
涤生大数据往期精彩推荐
8.SQL之优化篇:一文搞懂如何优化线上任务性能,增效降本!
10.基于FlinkSQL +Hbase在O2O场景营销域实时数仓的实践
12.涤生大数据实战:基于Flink+ODPS历史累计计算项目分析与优化(一)
13.涤生大数据实战:基于Flink+ODPS历史累计计算项目分析与优化(二)
14.5分钟了解实时车联网,车联网(IoV)OLAP 解决方案是怎样的?
15.企业级Apache Kafka集群策略:Kakfa最佳实践总结