Bitmaps位图
1.数据结构模型
现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位,例如“big”字符串是由3个字节组成,但实际在计算机存储时将其用二进制表示,“big”分别对应的ASCII码分别是98、105、103,对应的二进制分别是01100010、01101001和01100111
许多开发语言都提供了操作位的功能,合理地使用位能够有效地提高内存使用率和开发效率。Redis提供了Bitmaps这个“数据结构”可以实现对位的操作。把数据结构加上引号主要因为:
(1)Bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作。
(2)Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。
2.命令
我们使用用户是否访问过网站为例,介绍Bitmaps的各个命令。将访问的用户记做1,没有访问的用户记做0,用偏移量作为用户的id。
(1) 设置值
set key offset value
设置键的第offset个位的值(从0算起),假设现在有20个用户,userid=0,5,11,15,19的用户对网站进行了访问,那么当前Bitmaps初始化结果如图所示。
具体操作内容如下,使用visit:2024-11-28表示在2024年11月28日访问过的用户。
# 代码示例
> setbit visit:2024-11-28 0 1
(integer) 0
> setbit visit:2024-11-28 5 1
(integer) 0
> setbit visit:2024-11-28 11 1
(integer) 0
> setbit visit:2024-11-28 15 1
(integer) 0
> setbit visit:2024-11-28 19 1
(integer) 0
如果此时有一个userid=50的用户访问了网站,那么Bitmaps的结构变成了下图所示,第20位~49位都是0。
(2) 获取值
getbit key offset
获取用户0和用户8是否在2024年11月28日访问过。
> getbit visit:2024-11-28 0
(integer) 1
> getbit visit:2024-11-28 8
(integer) 0
(3) 获取Bitmaps指定范围内为1的个数
bitcount key [start end]
下面操作计算2024年11月28日这一天访问过的用户总数
> bitcount visit:2024-11-28
(integer) 5
start和end分别表示起始字节和结束字节,起始字节索引也是从0开始。
例如: bitcount visit:2024-11-28 1 3 表示计算位索引下标范围[8,31]之间为1的个数。
> bitcount visit:2024-11-28 1 3
(integer) 3
(4) Bitmaps间的运算
bitop AND | OR | XOR | NOT destkey key [key ...]
bitop是一个复合操作,他可以做多个Bitmaps的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在deskkey中。
假设:
2024年11月28日访问的userid = 0,5,11,15,19
2024年11月29日访问的userid = 1,2,5,9
下面计算两天都访问过的用户数量。
> bitop and visit:and:2024-11-28and2024-11-29 visit:2024-11-28 visit:2024-11-29
(integer) 3
> bitcount visit:and:2024-11-28and2024-11-29
(integer) 1
下面计算2024年1月28日和2024年11月29日任意一天都访问过的用户数量。
> bitop or visit:or:2024-11-28or2024-11-29 visit:2024-11-28 visit:2024-11-29
(integer) 3
> bitcount visit:or:2024-11-28or2024-11-29
(integer) 8
(5)计算Bitmaps中第一个值为targetBit的偏移量
bitpos key targetBit [start end]
下面操作获取2024-11-28当天访问的最小用户id
> bitpos visit:2024-11-28 1
(integer) 0
除此之外我们还可以使用start和end两个参数,分别代表起始字节和结束字节。
> bitpos visit:2024-11-28 1 1 3
(integer) 11
> bitpos visit:2024-11-28 1 2 3
(integer) 19
3.Bitmaps分析
假设有一个网站有1亿用户,每天独立访问的用户有5千万,如果使用集合类型和Bitmaps类型分别存储活跃用户的话可以得到下表。
数据类型 | 每个用户id占用空间 | 需要存储的用户量 | 全部内存量 |
---|---|---|---|
集合类型 | 64位 | 5千万 | 64位 * 5千万 = 400MB |
Bitmaps | 1位 | 1亿 | 1位 * 1亿 = 12.5MB |
很明显,在这种情况下使用Bitmaps可以节省下很多空间。这还是存储一天数据的情况下。随着时间的推移,差距会越来越明显。
数据类型 | 一天 | 一个月 | 一年 |
---|---|---|---|
set | 400M | 12G | 144G |
Bitmaps | 12.5M | 375M | 4.5G |
但是并不是什么情况下Bitmaps都比集合表现的好,如果每天独立访问的用户很少只有10万,那么两种数据类型的对比将如下表所示。
数据类型 | 每个用户id占用空间 | 需要存储的用户量 | 全部内存量 |
---|---|---|---|
集合类型 | 64位 | 10万 | 64位 * 10万 = 800KB |
Bitmaps | 1位 | 1亿 | 1位 * 1亿 = 12.5MB |
所以我们要根据不同的情况选择最适合的数据类型。