海量用户分群存储碾压实测:Array 彻底过时,ClickHouse Bitmap 存储节省100倍空间

一只会飞的鱼儿 2小时前 ⋅ 0 阅读
ad

前言

在用户行为分析、埋点平台、人群分群、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]

三大智能压缩策略

  1. 连续区间压缩(最核心) 连续ID:1,2,3,4,5 无需逐条存储,仅记录 起始值+长度,仅占用4字节即可替代5条数据

  2. 批量区间合并 连续ID:1000~1003,同样仅用一组起始+长度标识,海量连续用户ID极致瘦身

  3. 稀疏点原生存储 无规律离散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%

线上结论:绝大多数埋点、用户分群平台日活/总用户维持在千万级别以内,哈希碰撞概率可以直接忽略;如果是十亿级超大用户体量,可叠加业务前缀二次规避碰撞,成本极低。


八、最终总结

  1. Array(String):开发最简单,但存储爆炸、查询拉胯,海量分群场景直接淘汰

  2. Array(UInt64):小幅优化存储,但是无法解决人群运算慢的核心痛点,过渡方案

  3. Bitmap位图:ClickHouse用户分群场景终极最优解,百倍压缩、毫秒级人群计算,业内主流分析平台标准选型

如果你的业务正在面临用户分群存储占用过高、人群报表查询超时、服务器磁盘压力大等问题,直接迁移Bitmap存储,性价比和性能提升肉眼可见。

Webfunny用户分群正是采用了这种位图方式,大大降低了存储,提高性能。

Webfunny全链路监控埋点平台 是一站式前端监控 + 用户行为埋点 + 大数据分析平台,天然适配点位细查、用户行为回溯、批量导出等场景:

一体化架构:监控 + 埋点同一套 SDK,数据互通无壁垒
私有化部署:数据完全本地化,满足企业合规要求
高吞吐支撑:基于 ClickHouse 构建,亿级日志秒级查询
全端覆盖:H5 / 小程序 / APP / 鸿蒙全覆盖,统一导出口径
可定制强:支持接口扩展、分布式锁、限流降级等企业级能力

关于Webfunny

Webfunny专注于前端监控系统,前端埋点系统的研发。 致力于帮助开发者快速定位问题,帮助企业用数据驱动业务,实现业务数据的快速增长。支持H5/Web/PC前端、微信小程序、支付宝小程序、UniApp和Taro等跨平台框架。实时监控前端网页、前端数据分析、错误统计分析监控和BUG预警,第一时间报警,快速修复BUG!支持私有化部署,Docker容器化部署,可支持千万级PV的日活量!

  点赞 0   收藏 0
  • 一只会飞的鱼儿
    共发布82篇文章 获得9个收藏
全部评论: 0