原生 BitSet 不适合十亿签到场景——内存爆炸(10亿用户约125MB/标签,万标签即TB级)、不支持稀疏优化、ID非连续导致索引错乱、位运算慢(秒级)、无法分片和序列化;Redis Bitmap才是首选,配合MurmurHash映射与分层过滤可实现毫秒响应。

直接用原生 BitSet 在十亿级用户量下做签到统计和活跃过滤,**不可行**——它会因内存爆炸、无压缩、不支持稀疏优化而迅速失败。真正能落地的方案,是结合业务约束选对结构,并严格控制数据路径。
为什么原生 BitSet 不适合十亿签到场景
Java 的 BitSet 是纯内存 long[] 数组,10 亿用户对应约 125 MB / 标签。若系统有上万个标签(如每日签到、每周活跃、兴趣偏好),内存轻松突破 TB 级。更关键的是:
- 用户 ID 通常非连续(UUID、字符串、带空洞自增 ID),
BitSet要求 bit 位与用户 ID 严格一一映射,否则索引错乱 - 签到数据天然稀疏(比如日活仅占全量用户的 5%~10%),
BitSet仍分配全部空间,无法节省内存 -
and()/or()是逐 long 循环执行,百亿 bit 运算耗时在秒级甚至更高,达不到“秒级响应”要求 - 不支持序列化传输、无法分片、不能嵌入 OLAP 引擎(如 ClickHouse、Greenplum),难以对接实时分析链路
Redis Bitmap 是十亿签到统计的首选方案
Redis 的 SETBIT/BITCOUNT/BITOP 天然适配签到类二值统计,底层基于 String,最大支持 offset ≈ 42.9 亿,空间占用可精准估算:
- 10 亿用户 → 最大 offset = 10⁹ → 占用 ≈ 10⁹ ÷ 8 ÷ 1024² ≈ 119 MB
- 每日一个 key(如
sign:20260424),签到即SETBIT sign:20260424 {uid} 1 - 查 DAU 直接
BITCOUNT sign:20260424,毫秒完成 - 查周活:用
BITOP OR week_bitmap sign:20260418 sign:20260419 ... sign:20260424合并再BITCOUNT
所有操作原子、低延迟、支持分布式,无需额外映射服务——前提是用户 ID 可转为整数 offset。
当用户 ID 是字符串或 UUID 时,必须做轻量映射
不能直接哈希取模后写入 Redis Bitmap(会引入哈希冲突风险),也不能查 MySQL/Redis 映射表(增加 RT 和瓶颈)。推荐做法:
- 使用强一致性哈希函数(如
MurmurHash3_x64_128)将 UID 转为 64 位 long,再对位图容量取模(如 mod 10⁹) - 映射逻辑内嵌在应用层,不走远程调用;冲突概率极低(≈ 1/2⁶⁴),业务可接受
- 若需精确去重(如防止误判签到),可在关键路径加一层布隆过滤器预检,但非必需
多维活跃过滤要避免中间态膨胀
例如筛选「北京 + 25–30 岁 + 近 7 日签到过」,不能简单把三个 Bitmap 全部加载再 or/or/and。正确策略:
- 优先用强约束条件缩小范围:先取
city_beijing和age_25_30的交集(BITOP AND),得到中等规模 bitmap - 再与
week_sign做 and,而非把三者全 or 后再 and - 高频组合(如「北上广深 + 20–35 岁」)可预计算并缓存为新 key,减少实时运算压力
- 单次请求限制中间 bitmap 大小(如 >5000 万 bit 则拒绝或降级),防 OOM
不复杂但容易忽略:真正的秒级响应,靠的不是算法多炫,而是让所有位运算发生在内存或 Redis 内核中,全程零磁盘 IO、零远程调用、零对象构造开销。










