一、什么是 BitMap?
BitMap(位图) 是 Redis 中一种特殊的数据结构,它并不是独立的数据类型,而是基于 String 类型实现的二进制位操作。每个位(bit)只能存储 0 或 1,通过偏移量(offset)来定位具体的位。以下是其底层实现原理:
数据结构
bash
c
// Redis 字符串对象结构(简化)
struct SDS {
char buf[]; // 字节数组
size_t len; // 长度
size_t free; // 空闲空间
}
内存布局示例
bash
text
偏移量: 0 1 2 3 4 5 6 7 8 9 10 11
字节: [0 1 0 1 1 0 1 0 0 1 0 1]
位值: 0 1 0 1 1 0 1 0 0 1 0 1
索引: ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
offset: 0 1 2 3 4 5 6 7 8 9 10 11
内存计算
-
每个位占 1 bit
-
1 字节 = 8 bits
-
偏移量
offset范围:0 到 2^32-1(42.9亿) -
最大占用内存:约 512 MB(2^32 bits = 512 MB)
二、BitMap 的主要使用场景:
1. 用户行为统计与标记
场景
-
记录用户是否执行过某个操作(如是否登录、是否领取奖励)。
-
标记用户属性(如是否VIP、是否启用通知)。
示例
bash
# 记录用户ID为1001的用户已登录
SETBIT user:login:20250130 1001 1
# 检查用户1001今天是否登录
GETBIT user:login:20250130 1001
# 统计今天登录用户数
BITCOUNT user:login:20250130
2. 活跃用户统计(如 DAU/MAU)
场景
-
统计每日/每月活跃用户数。
-
使用多个 BitMap 进行集合运算(如计算连续活跃用户)。
示例
bash
# 记录2025-01-30和2025-01-31的活跃用户
SETBIT active:20250130 1001 1
SETBIT active:20250131 1002 1
# 计算两天都活跃的用户(交集)
BITOP AND active:both_days active:20250130 active:20250131
BITCOUNT active:both_days
3. 布隆过滤器(Bloom Filter)实现
场景
-
用于快速判断元素是否存在(如防缓存穿透、爬虫URL去重)。
-
可能存在误判(假阳性),但节省内存且速度快。
示例原理
使用多个哈希函数将元素映射到 BitMap 的不同位,检查时若所有位均为1则可能存在。
4. 实时数据监控与标记
场景
-
设备在线状态监控(每个位代表一个设备ID)。
-
系统错误标记(按时间片记录错误发生情况)。
示例
bash
# 监控1000台设备,ID 50的设备上线
SETBIT devices:online 50 1
# 批量检查设备状态
BITFIELD devices:online GET u1 50 GET u1 51
5. 节省空间的布尔值存储
场景
-
当需要存储大量布尔值(是/否)时,BitMap 比 Set/Hash 更省内存。
-
例如:10亿用户的签到记录仅需约 120 MB(而 Set 需要 GB 级别)。
内存对比
-
1亿用户签到记录:
-
BitMap:约
1亿 / 8 / 1024² ≈ 12 MB -
Set:每个用户ID占约8字节,约
8 * 1亿 / 1024² ≈ 763 MB
-
6. 时间序列数据压缩存储
场景
-
记录用户每小时的在线状态(24位即可存一天)。
-
配合
BITFIELD命令进行紧凑存储和读取。
示例
bash
# 用24位存储用户1001一天每小时在线状态(1在线,0离线)
BITFIELD user:1001:20250130 SET u1 0 1 SET u1 1 0 ... SET u1 23 1
# 读取第10小时的状态
BITFIELD user:1001:20250130 GET u1 10
7. 特征标记与AB测试
场景
-
用户分群标记(如"男性+90后+喜欢科技")。
-
A/B测试分组(每个位代表一个实验分组)。
三、其他事项及总结
注意事项
-
适合大数据量布尔值:数据量较小时可直接用 Set/Hash。
-
位偏移量限制:偏移量(offset)最大为 2^32-1(约42亿)。
-
稀疏数据可能浪费内存:如果位分布非常稀疏,可能不如 Set 节省内存。
-
持久化考虑:RDB 持久化时 BitMap 会存为字符串,AOF 会记录位操作命令。
经典案例:用户签到系统
bash
# 用户1001在2025年1月30日签到
SETBIT sign:1001:202501 30 1 # 月份为键,日期为偏移量
# 统计当月签到天数
BITCOUNT sign:1001:202501
# 获取连续签到情况(使用BITFIELD或BITPOS)
BITFIELD sign:1001:202501 GET u31 0 # 获取前31位数据
常用命令
bash
SETBIT key offset 0/1 # 设置位
GETBIT key offset # 获取位
BITCOUNT key [start end] # 统计1的个数
BITOP AND/OR/XOR dest key1 key2 # 位运算
BITPOS key bit [start] [end] # 查找第一个0/1的位置
BITFIELD key [GET/SET/INCRBY] # 位域操作
BitMap 在需要高性能、低内存消耗的布尔值存储和统计场景中极具优势,但在复杂查询或非二进制场景下需结合其他数据结构使用。