BitMap的使用

Redis Bitmap 是什么?

Redis Bitmap 本质上并不是一种独立的数据结构,它底层仍然是 Redis 的 String 类型

Redis 将一个字符串的每个二进制位(bit)当成一个数组元素来操作:

  • 一个 bit 只有两种状态:0 或 1
  • 1 Byte = 8 bit
  • Redis 可以直接对指定偏移量(offset)的 bit 进行读写

例如:

 
偏移量:0 1 2 3 4 5 6 7
值 :1 0 1 0 0 1 1 0
 

实际上存储的只是一个字节:

 
10100110
 

Bitmap 的核心命令

设置某个位

 
SETBIT user:sign:202506 0 1
SETBIT user:sign:202506 1 1
SETBIT user:sign:202506 2 0
 

表示:

 
第0位 = 1
第1位 = 1
第2位 = 0
 

获取某个位

 
GETBIT user:sign:202506 1
 

返回:

 
1
 

统计1的数量

 
BITCOUNT user:sign:202506
 

返回:

 
15
 

表示有15个位为1。


位运算

支持:

 
BITOP AND
BITOP OR
BITOP XOR
BITOP NOT
 

例如:

 
BITOP AND result key1 key2
 

求两个 Bitmap 的交集。


Bitmap 原理

假设有 1 亿用户:

 
userId:
1
2
3
...
100000000
 

使用 Bitmap:

 
offset = userId
 

如果用户登录:

 
SETBIT login:20250601 100000000 1
 

表示:

 
用户100000000今天登录了
 

如果没登录:

 
bit = 0
 

存储空间计算

1亿用户:

 
100000000 bit
 

换算:

 
100000000 / 8
≈ 12.5 MB
 

只需要:

 
12.5MB
 

即可记录 1 亿用户是否登录。


为什么 Bitmap 节省空间?

Set方案

如果用 Set:

 
SADD login:20250601 user1 user2 user3
 

假设:

 
一个用户ID占8字节
 

1亿用户:

 
8 × 100000000
≈ 800MB
 

实际 Redis 对象开销更大,可能:

 
1~2GB
 

Bitmap方案

 
100000000 bit
≈ 12.5MB
 

节省几十到上百倍空间。


应用场景

场景1:用户签到

最经典场景。

记录签到

2025年6月:

 
1号签到
2号签到
5号签到
 

存储:

 
位置:
1 2 3 4 5

值:
1 1 0 0 1
 

代码:

 
SETBIT sign:10001:202506 1 1
SETBIT sign:10001:202506 2 1
SETBIT sign:10001:202506 5 1
 

查询是否签到

 
GETBIT sign:10001:202506 5
 

返回:

 
1
 

表示已签到。


统计签到天数

 
BITCOUNT sign:10001:202506
 

返回:

 
3
 

场景2:连续签到

例如:

 
111110
 

表示:

 
连续签到5天
 

可以通过:

 
BITFIELD
 

取出整个整数后进行位运算。

Java示例:

 
long signData = ...;

int count = 0;

while ((signData & 1) == 1) {
count++;
signData >>= 1;
}
 

统计连续签到天数。


场景3:用户活跃统计(DAU)

记录每日登录:

 
SETBIT dau:20250601 userId 1
 

统计当天活跃用户:

 
BITCOUNT dau:20250601
 

即可得到:

 
DAU
 

场景4:在线用户统计

例如:

 
用户ID -> bit位置
 

上线:

 
SETBIT online:user 10001 1
 

下线:

 
SETBIT online:user 10001 0
 

统计在线人数:

 
BITCOUNT online:user
 

场景5:用户标签系统

例如:

 
是否VIP
是否实名认证
是否绑定手机
 

分别建立 Bitmap:

 
vip:user
real:user
phone:user
 

用户:

 
10001
 

设置:

 
SETBIT vip:user 10001 1
SETBIT real:user 10001 1
 

查询:

 
VIP 且 实名用户
 
 
BITOP AND result vip:user real:user
BITCOUNT result
 

场景6:用户画像分析

统计:

 
今天登录
昨天登录
购买过商品
充值过
 

分别建立 Bitmap:

 
login:today
login:yesterday
pay:user
recharge:user
 

例如:

求留存用户

昨天登录且今天登录:

 
BITOP AND retain login:yesterday login:today
 

统计:

 
BITCOUNT retain
 

求新增用户

今天登录但昨天没登录:

 
BITOP XOR diff login:yesterday login:today
 

场景7:海量布尔状态存储

例如:

 
消息已读/未读
优惠券领取/未领取
任务完成/未完成
 

都适合 Bitmap。

例如:

 
SETBIT coupon:10001 couponId 1
 

表示:

 
用户10001领取了该优惠券
 

Bitmap 的局限性

1. 只能表示二值状态

只能:

 
0
1
 

不能直接存:

 
年龄
金额
昵称
 

2. 用户ID过大浪费空间

例如:

 
最大用户ID = 1000000000
实际用户数 = 1000
 

那么:

 
需要分配10亿个bit
≈125MB
 

大量空间被浪费。

因此 Bitmap 更适合:

 
连续ID
稠密ID
 

不适合:

 
UUID
雪花ID
随机ID
 

3. 删除困难

 
SETBIT key offset 0
 

只是置0。

Redis 不会自动缩容。


Bitmap 与 HyperLogLog 对比

特性BitmapHyperLogLog
是否精确 精确 近似
去重统计 可以 专门用于去重
判断用户是否存在 可以 不可以
统计人数 精确 误差约0.81%
存储空间 随用户数增长 固定约12KB
适用场景 签到、活跃用户 UV统计

例如:

DAU(要求精确)

使用:

 
Bitmap
 

网站UV(允许误差)

使用:

 
HyperLogLog
 

总结

Bitmap 的核心思想就是:

 
一个用户(或对象)对应一个 bit
 

利用 1 bit 记录一个布尔状态,以极低的内存成本实现:

  • 用户签到
  • DAU统计
  • 在线用户统计
  • 用户标签
  • 用户画像分析
  • 留存分析
  • 消息已读状态

其最大优势是:

 
空间极小
查询极快
支持位运算
 

因此在互联网业务中,Bitmap 经常用于 海量用户状态统计,而 HyperLogLog 更偏向于 海量用户去重计数(UV)

 

posted on 2026-07-05 15:09  日思日睿  阅读(9)  评论(0)    收藏  举报