一、引言:为什么 Bitmaps 是高效位操作的利器?
在高并发、海量用户的互联网系统中,如何以极低内存开销实现"用户是否在某天签到"这类布尔状态的存储与统计?
传统方案如关系型数据库或 Set 结构往往面临存储膨胀或查询效率瓶颈。而 Redis 的 Bitmaps(位图) 正是为此类场景量身打造的底层优化数据结构。
尽管 Redis 并未将 Bitmaps 视为独立的数据类型(它本质是 String 类型的位级操作视图 ),但其通过位运算实现了 O(1) 级别的读写性能与极致压缩的内存占用------1 亿用户每日签到仅需约 12MB 内存。
二、Bitmaps 底层原理:String 上的位魔法
1. 数据结构本质
- Bitmaps 并非 Redis 的独立数据类型,而是对 String 类型的按位操作接口。
- Redis 中的 String 最大可存储 512MB(即 2322^{32}232 位),每一位(bit)可独立设置为 0 或 1。
- 内部使用 SDS(Simple Dynamic String) 存储,支持动态扩容,但位操作本身不触发额外内存分配(除非超出当前长度)。
2. 内存效率分析
| 用户规模 | 所需位数 | 所需字节数 | 实际内存(含 Redis 对象开销) |
|---|---|---|---|
| 1 万 | 10,000 | 1,250 | ≈ 1.5 KB |
| 100 万 | 1M | 125 KB | ≈ 130 KB |
| 1 亿 | 100M | 12.5 MB | ≈ 13 MB |
对比:若用 Hash 存储 {user_id: true/false},1 亿用户需数 GB 内存。
三、用户签到系统设计:Bitmaps 的典型应用
1. 设计思路
- Key 命名 :
sign:{YYYYMM}:{user_id}(例如sign:202603:10086) - 位偏移(offset):以当月第几天 -1 作为 offset(如 3 月 10 日 → offset=9)
- 值含义:bit=1 表示已签到,0 表示未签到
2. 核心操作流程

3. 关键命令详解
| 命令 | 语法 | 时间复杂度 | 用途说明 |
|---|---|---|---|
SETBIT |
SETBIT key offset value |
O(1) | 设置某位为 0/1 |
GETBIT |
GETBIT key offset |
O(1) | 获取某位值 |
BITCOUNT |
BITCOUNT key [start end] |
O(N) | 统计 1 的个数(N=字节数) |
BITPOS |
BITPOS key bit [start end] |
O(N) | 查找第一个 0/1 的位置 |
BITOP |
BITOP AND/OR/XOR/NOT destkey key... |
O(N) | 多 Bitmaps 位运算(用于交并集) |
注意:
BITCOUNT和BITPOS的start/end单位是 字节,不是位!
四、Bitmaps vs 其他数据结构:签到场景对比
| 方案 | 内存占用 | 查询单日签到 | 统计月签到天数 | 支持连续签到分析 | 适用规模 |
|---|---|---|---|---|---|
| Bitmaps | 极低(~13MB/亿用户) | O(1) | O(N)(N=31字节) | 需 BITPOS + 逻辑 |
超大规模 |
| Set | 高(每个元素独立存储) | O(1)(SISMEMBER) | O(1)(SCARD) | 难(需排序) | 中小规模 |
| Sorted Set | 极高(带 score) | O(log N) | O(log N) | 可(ZRANGEBYSCORE) | 小规模+需排序 |
结论:Bitmaps 在纯签到/活跃状态场景下综合优势显著。
五、其他典型应用场景
- DAU/MAU 统计 :每天一个 Bitmap,
BITOP OR合并得到月活跃用户。 - 布隆过滤器辅助:用多个 Bitmaps 模拟多哈希函数。
- 权限控制矩阵:用户 ID 为行,权限位为列,实现细粒度授权。
- 实时在线状态:每小时一个 Bitmap,快速判断用户是否在某时段在线。
六、高频面试题
Q1:Bitmaps 是 Redis 的独立数据类型吗?
答:不是。Bitmaps 是对 String 类型的位操作抽象,底层仍是 SDS 字符串。
Q2:如何用 Bitmaps 统计某用户本月连续签到的最大天数?
答 :需遍历位图查找最长连续 1 序列。可用 GETRANGE 获取整个月的字节,再在应用层解析;或结合 Lua 脚本在 Redis 内完成(避免网络往返)。
Q3:BITCOUNT key 0 0 和 BITCOUNT key 有什么区别?
答:前者只统计第 0 字节(8 位)中 1 的个数;后者统计整个字符串所有位。
Q4:Bitmaps 支持负数 offset 吗?
答 :不支持。offset 必须 ≥ 0,且最大为 232−12^{32}-1232−1(受 String 长度限制)。
Q5:多个 Bitmaps 如何求"本月每天都签到的用户"?
答 :对每天的 Bitmap 执行 BITOP AND result_key day1 day2 ... day31,结果中 bit=1 的用户即为全勤用户。