海量用户分群存储碾压实测:Array 彻底过时,ClickHouse Bitmap 存储节省100倍空间
前言
在用户行为分析、埋点平台、人群分群、UV统计等业务场景中,我们经常需要存储整个人群包的用户ID列表,用于后续人群圈选、人群交集/并集计算、留存分析、报表复盘。
绝大多数业务初期都会直接使用 Array(String) 存储用户UUID,开发简单、上手零成本,但随着分群数量上涨、单分群用户体量突破十万/百万级别,会暴露出致命问题:存储空间爆炸、ARRAY JOIN查询缓慢、人群运算SQL极其复杂。
本文基于真实业务数据,横向对比 Array(String)、Array(UInt64)、Bitmap 三种存储方案,从存储体积、查询性能、人群运算、维护成本全方位实测,帮大家搞定海量人群分群的最优存储架构。
一、业务实测场景说明
测试基准口径(贴合线上埋点真实业务)
-
平台总用户量:100万
-
分群A:10万用户(占比10%)
-
分群B:50万用户(占比50%)
-
分群C:1万用户(占比1%)
-
业务常规规模:平台同时维护30个业务分群
分别使用三种主流方案存储全量用户ID,统一计算存储空间占用,直观对比差异。
二、三种存储方案空间占用实测对比
方案一:Array(String) 字符串数组(当前绝大多数业务在用)
直接存储原生UUID字符串,单条UUID固定占用36字节,无任何压缩,原生存储。
-
分群A(10万用户):10万 × 36字节 = 3.6 MB
-
分群B(50万用户):50万 × 36字节 = 18 MB
-
分群C(1万用户):1万 × 36字节 = 360 KB
30个分群总存储空间:≈ 150 MB
核心痛点:字符串本身体积大,无压缩能力,分群越多,存储成本直线飙升;后续人群关联查询必须使用ARRAY JOIN,大数组关联查询耗时极高。
方案二:Array(UInt64) 数字数组(字符串转数字ID优化)
将原始字符串UUID映射为64位整型数字,单条数据仅占用8字节,相比字符串直接缩小4倍体积。
-
分群A(10万用户):10万 × 8字节 = 800 KB
-
分群B(50万用户):50万 × 8字节 = 4 MB
-
分群C(1万用户):1万 × 8字节 = 80 KB
30个分群总存储空间:≈ 45 MB
核心痛点:存储压力有所缓解,但依旧没有解决查询性能问题;人群交集、并集、差集依旧需要复杂SQL遍历数组计算,大分群运算效率依旧拉胯。
方案三:Roaring Bitmap 位图存储(ClickHouse最优解)
依托RoaringBitmap极致压缩算法,依托连续区间压缩存储,不再逐条存储用户ID,空间压缩比达到百倍级别。
-
分群A(10万用户):压缩后仅 10-50 KB
-
分群B(50万用户):压缩后仅 50-200 KB
-
分群C(1万用户):压缩后仅 1-5 KB
30个分群总存储空间:≈ 1.5 MB
碾压级差距:相比原生字符串数组,存储空间直接节省100倍;百万级用户分群,位图存储体积甚至不足200KB。
三、深度拆解:为什么Bitmap压缩能力碾压普通数组?
很多同学疑惑:同样存储100万用户ID,位图为什么能做到近乎无损极致压缩?核心依托RoaringBitmap三大压缩策略,贴合用户ID连续分布的业务特征:
原始待存储数据示例
[1, 2, 3, 4, 5, 1000, 1001, 1002, 1003, 50, 200, 500]
三大智能压缩策略
-
连续区间压缩(最核心) 连续ID:1,2,3,4,5 无需逐条存储,仅记录
起始值+长度,仅占用4字节即可替代5条数据 -
批量区间合并 连续ID:1000~1003,同样仅用一组起始+长度标识,海量连续用户ID极致瘦身
-
稀疏点原生存储 无规律离散ID:50、200、500,才会单独数组存储,业务中稀疏数据占比极低
关键结论:用户行为ID大多呈连续分布特征,刚好完美适配Bitmap压缩逻辑;哪怕是100万用户的完整人群包,压缩后体积也仅在50~200KB区间。
四、ClickHouse原生Bitmap开箱即用代码
ClickHouse内置完整Bitmap聚合函数与运算能力,无需额外依赖、无需中间件,一行SQL即可完成人群建表、写入、统计、人群交叉计算。
1. 创建分群结果专属位图表
CREATE TABLE segment_users (
segmentId String COMMENT '分群唯一ID',
user_bitmap AggregateFunction(groupBitmap, UInt64) COMMENT '位图存储人群用户ID'
) ENGINE = AggregatingMergeTree()
ORDER BY segmentId;
2. 写入数据:批量构建用户位图
INSERT INTO segment_users
SELECT 'seg1', groupBitmapState(toUInt64(user_id))
FROM (SELECT arrayJoin([1, 2, 5, 100, 1000]) as user_id);
3. 常用业务查询SQL(覆盖全场景)
-- 1. 统计单个分群总UV人数
SELECT bitmapCardinality(user_bitmap) as user_count
FROM segment_users
WHERE segmentId = 'seg1';
-- 2. 两个分群人群交集(重合用户数)
SELECT bitmapAndCardinality(seg1_bitmap, seg2_bitmap) as overlap_user_count;
-- 3. 判断单个用户是否在指定分群内
SELECT bitmapContains(user_bitmap, toUInt64(100)) as is_in_segment
FROM segment_users
WHERE segmentId = 'seg1';
-- 4. 分群人群并集、差集
SELECT bitmapOrCardinality(bitmap1, bitmap2); -- 并集
SELECT bitmapXorCardinality(bitmap1, bitmap2); -- 差集
五、三大方案全维度横向对比
|
对比维度 |
Array(String) |
Array(UInt64) |
Bitmap位图 |
|---|---|---|---|
|
存储空间 |
❌ 极大,10万用户3.6MB |
⚠️ 中等,10万用户800KB |
✅ 极小,10万用户仅10-50KB |
|
查询性能 |
⚠️ ARRAY JOIN 关联查询极慢 |
⚠️ 依旧依赖数组遍历,性能一般 |
✅ 原生位图运算,百万级毫秒返回 |
|
数据去重能力 |
❌ 需要业务手动去重 |
❌ 需要业务手动去重 |
✅ 位图天然自动去重 |
|
人群交叉计算 |
❌ SQL极度复杂,性能极差 |
❌ 数组遍历计算,耗时高 |
✅ 原生函数支持交集/并集/差集 |
|
ClickHouse兼容性 |
✅ 原生支持 |
✅ 原生支持 |
✅ 原生官方函数,无兼容坑 |
|
开发实现复杂度 |
✅ 最低,直接存储原始UUID |
⚠️ 需要字符串ID转数字ID |
⚠️ 需要ID映射+小幅改造 |
六、线上落地建议:最优架构方案
推荐落地表结构(可直接上线使用)
CREATE TABLE {projectId}_segment_results (
segmentId String COMMENT '分群ID',
user_bitmap AggregateFunction(groupBitmap, UInt64) COMMENT '位图存储整个人群包'
) ENGINE = AggregatingMergeTree()
ORDER BY segmentId;
Bitmap方案优缺点复盘
✅ 核心优势
-
存储成本暴跌:相比字符串数组,压缩比最高可达100倍
-
人群运算零开发:内置函数直接完成交集、并集、留存计算
-
天然去重:无需业务层额外处理重复用户数据
-
查询性能拉满:位图底层位运算,大数据量碾压数组查询
⚠️ 现存短板
-
业务原始ID大多为字符串UUID,需要映射为UInt64数字
-
写入、查询逻辑需要小幅改造,无法零成本无缝切换
七、落地关键答疑:字符串UUID转UInt64哈希碰撞风险?
问题
业务现有weCustomerKey为字符串UUID,使用 cityHash64 函数将字符串转为UInt64数字,会不会出现哈希碰撞,导致用户ID混淆?
风险概率测算(基于生日悖论公式)
64位哈希空间极大,不同用户量下碰撞概率极低,完全满足埋点分析业务线上要求:
-
用户量100万:碰撞概率 ≈ 0.000003%
-
用户量1000万:碰撞概率 ≈ 0.00027%
-
用户量1亿:碰撞概率 ≈ 0.027%
-
用户量10亿:碰撞概率 ≈ 2.7%
线上结论:绝大多数埋点、用户分群平台日活/总用户维持在千万级别以内,哈希碰撞概率可以直接忽略;如果是十亿级超大用户体量,可叠加业务前缀二次规避碰撞,成本极低。
八、最终总结
-
Array(String):开发最简单,但存储爆炸、查询拉胯,海量分群场景直接淘汰
-
Array(UInt64):小幅优化存储,但是无法解决人群运算慢的核心痛点,过渡方案
-
Bitmap位图:ClickHouse用户分群场景终极最优解,百倍压缩、毫秒级人群计算,业内主流分析平台标准选型
如果你的业务正在面临用户分群存储占用过高、人群报表查询超时、服务器磁盘压力大等问题,直接迁移Bitmap存储,性价比和性能提升肉眼可见。
Webfunny用户分群正是采用了这种位图方式,大大降低了存储,提高性能。
Webfunny全链路监控埋点平台 是一站式前端监控 + 用户行为埋点 + 大数据分析平台,天然适配点位细查、用户行为回溯、批量导出等场景:
一体化架构:监控 + 埋点同一套 SDK,数据互通无壁垒
私有化部署:数据完全本地化,满足企业合规要求
高吞吐支撑:基于 ClickHouse 构建,亿级日志秒级查询
全端覆盖:H5 / 小程序 / APP / 鸿蒙全覆盖,统一导出口径
可定制强:支持接口扩展、分布式锁、限流降级等企业级能力
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)