Bitmap
一、类型概述
Bitmap(位图),也称为 Bit array(位数组)或 Bitset(位集合),并不是一种单独的数据结构,而是基于 Redis 的 String 类型实现的一种"位级别"的操作方式。
你可以把它想象成一个由 0
和 1
组成的数组,数组的每个单元只能存储 0
或 1
,数组的索引(在 Redis 中称为 offset
)从 0 开始。
核心要点:
- 底层是 String:Bitmap 的本质是一个 String 值,它对应的是一个二进制的字节串。所以,所有针对 Bitmap 的命令,本质上都是对字符串值的二进制位进行操作。
- 自动扩容:当你设置一个偏移量很大的位时,Redis 会自动分配内存并将中间未设置的位全部初始化为 0。
- 极其节省空间:通过位来存储信息,一个字节(8 bits)可以存储 8 个独立的状态。例如,存储 1 亿用户的在线状态,只需要大约 12MB 的内存(1亿 / 8 / 1024 / 1024 ≈ 11.92MB)。
二、基本方法
1. 设置位值 SETBIT key offset value
bash
127.0.0.1:6379> setbit log 0 1
(integer) 0 #返回指定偏移量原来存储的位,没有默认为0。
127.0.0.1:6379> setbit log 1 1
(integer) 0
127.0.0.1:6379> setbit log 2 1
(integer) 0
127.0.0.1:6379> setbit log 3 0
(integer) 0
2. 获取位值 GETBIT key offset
bash
127.0.0.1:6379> getbit log 2
(integer) 1
3. 统计 BITCOUNT key [start end]
start
和 end
是字节索引,而不是位索引。可以使用负数值,-1 表示最后一个字节,-2 表示倒数第二个字节。
bash
127.0.0.1:6379> bitcount log
(integer) 3 #位值位1的有三个
三、内部实现
正如概述中所说,Bitmap 的底层实现就是 Redis 的 String 数据类型。String 在 Redis 内部是以二进制安全的字节数组形式存储的。
当你执行 SETBIT key 10 1
时:
- Redis 会找到或创建一个与
key
关联的字符串值。 - 它会计算出第 10 位位于哪个字节(第
10 / 8 = 1
个字节,索引从 0 开始)以及在该字节中的哪一位(第10 % 8 = 2
位)。 - 然后,它通过位运算(如按位或
|
和按位与&
)来设置或清除该特定位的值。 - 如果目标偏移量超出了当前字符串的长度,Redis 会扩展字符串,并用零字节填充中间的空隙。
四、应用场景
Bitmap 因其极致的空间效率和快速的位运算,在特定场景下表现非常出色。
1. 用户行为标记
这是最经典的应用场景。
- 用户签到 :
key
可以设计为user:sign:202405:{uid}
,一位代表一天。SETBIT user:sign:202405:1001 20 1
表示用户 1001 在 5 月 21 日签到了。 - 用户在线状态 :
key
可以设计为user:online:20240520
,一位代表一个用户。SETBIT user:online:20240520 1001 1
表示用户 1001 今天在线。
2. 大数据量下的布尔统计
-
活跃用户分析 :如上例,可以轻松统计每日活跃用户数(
BITCOUNT
)。通过BITOP AND/OR
,可以计算:- 连续活跃用户 :
BITOP AND
- 某段时间内的活跃用户 :
BITOP OR
- 连续活跃用户 :
-
特征标签筛选 :可以为每个特征(如"男性"、"喜欢编程"、"VIP用户")创建一个 Bitmap,每一位表示一个用户是否具有此特征。通过
BITOP AND
可以快速找到同时满足多个特征的用户群。
3. 布隆过滤器
Bitmap 是实现布隆过滤器的核心数据结构。布隆过滤器是一种概率型数据结构,用于高效地判断一个元素是否存在于一个集合中。它可能存在误判(判断一个不存在的元素为存在),但绝不会漏判(判断存在的元素一定存在),并且非常节省空间。