跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何利用 BitSet 位图算法在十亿级用户画像系统中实现秒级的标签过滤与属性筛选

原生 BitSet 不适用于十亿级用户画像标签过滤,因其内存占用大(125 MB/标签)、不支持稀疏优化、无高效交并补运算、不可分片与序列化;RoaringBitmap 通过容器分层、并行计算、紧凑序列化和引擎集成解决上述问题。 BitSet 本身不适用于十亿级用户画像的标签过滤——它在单机内存中无法承载 10⁹ 量级的 bit(需约 125 MB / 标签,且无法压缩、无交并补优化),实际生产系统早已弃用原生
BitSet
,转而采用 RoaringBitmap 或类似结构。 为什么原生
BitSet
在十亿场景下直接失效 Java 的
BitSet
是纯内存、非压缩、稀疏效率差的数据结构: 10 亿用户对应
BitSet
长度为 10⁹,底层用 long[] 存储,需约 125 MB 内存 / 标签;10 万标签即超 12 TB 内存,完全不可行 不支持稀疏位集优化:若一个标签只覆盖 500 万用户(0.5% 稀疏度),
BitSet
仍分配全部空间,而 RoaringBitmap 此时仅占 ~12 MB 无内置交/并/差集的向量化计算能力,
and()
/
or()
是逐 word 循环,百亿 bit 操作耗时秒级起跳 无法分片、无法序列化传输、不兼容分布式查询(如 Greenplum、ClickHouse 插件)
RoaringBitmap
替代
BitSet
的关键改造点 真正落地十亿级标签筛选的,是
RoaringBitmap
—— 它不是“BitSet 的升级版”,而是从设计上就面向海量稀疏布尔集合的压缩位图结构: 自动按数据密度切分:对连续段用
ArrayContainer
(小范围高效),对密集段用
BitmapContainer
(64KB 固定大小),对超大范围用
RunContainer
(游程编码) 所有集合运算(
and()
、
or()
、
xor()
、
andNot()
)均基于 container 粒度并行,实测 10 亿用户 × 10 万标签交集可在毫秒内完成 支持序列化为紧凑字节数组,可直接落盘或通过网络传输;Greenplum、ClickHouse 均有官方或社区
roaringbitmap
扩展支持 与列式引擎天然契合:例如 ClickHouse 的
AggregateFunction(groupBitmap, UInt32)
可将用户 ID 流实时聚合成 bitmap,后续
bitmapAnd()
即标签圈选 在 Greenplum + RoaringBitmap 中实现标签圈选的最小可行路径 这不是“写个 Java 工具类”能解决的问题,必须嵌入 OLAP 引擎执行层。以 Greenplum 为例(参考 2019 年 digoal 方案演进版): 建表时用
roaringbitmap
类型存储每个标签的用户集合:
CREATE TABLE user_tags (tag_id INT, bitmap roaringbitmap)
导入数据前先按用户 ID 分桶(如 mod 1000),再批量调用
rb_build()
构造 bitmap,避免单次构造超时 圈选逻辑直接 SQL 化:
SELECT rb_cardinality(rb_and(bitmap, (SELECT bitmap FROM user_tags WHERE tag_id = 123))) FROM user_tags WHERE tag_id = 456
务必开启分区:按
tag_id
或时间范围分区,否则全表扫描 bitmap 列会退化为磁盘 IO 密集型操作 避免在应用层做多次
rb_and()
拼接——应尽量收拢为一条 SQL,让 GP 的 MPP 引擎并行执行容器级运算 容易被忽略的三个硬伤点 即便用了
RoaringBitmap
,以下三点仍会导致查询从毫秒退化到秒级甚至超时: 用户 ID 未归一为 uint32:RoaringBitmap 对 >2³² 的 key 支持极差,10 亿用户必须映射到
[0, 10^9)
范围内,不能直接用微信 OpenID 或手机号哈希值(可能超界) 未预热 bitmap 缓存:GP 中 roaringbitmap 运算虽快,但首次加载大 bitmap 仍需解压+构建 container 索引,冷查询延迟抖动明显;建议用
rb_cardinality()
预触一次热点标签 误把 bitmap 当明细表用:有人试图用
rb_iterate()
拉出全部用户 ID 再 Join 业务表——这等于放弃位图优势;正确做法是用
rb_contains()
或子查询关联,让引擎在 bitmap 层完成过滤 真正卡住十亿画像系统响应速度的,从来不是算法理论,而是 ID 映射是否连续、存储是否分区、SQL 是否收敛到 bitmap 原语这三个实操细节。

相关文章