本文介绍redis的新数据类型之一:bitmaps。它在当今的互联网环境当中有很多的应用场景,比如常见的签到、点赞、日活等等。下面我们来具体看下它到底是什么。

一、什么是BitMap

我们都知道bit,是计算机当中最小的存储单位,8个bit组成一个Byte,而bit只能存储两个值,0和1。

BitMap可以理解为存储bit的数组,多个bit存储后组成的一个特定结构,每个位置只能存储1和0。

那么使用bitmap有什么好处呢?

先给结论,节省内存空间

我们知道在java当中,一个int类型占用4个字节,即4个Byte。假设有‘1’,‘3’,‘5’三个数字,如果存储在HashMap当中,需要 3 * 4 * 8 = 96 bit 的空间存储

而如果将他们存储在BitMap当中呢?

如上图所示,总共6位,可以存储从0~5的整数,相比HashMap的存储方式,足足缩减了16倍的存储空间。

二、Redis的Bitmaps

Redis提供了Bitmaps的数据类型,让我们可以实现对位的操作。

Bitmaps其实属于redis字符串的一种,它的出现只是让我们可以对字符串按位操作。并且redis为它提供了单独的一套操作命令,相当于一个元素是bit的数组,每个位置只能存储0和1,其有从0开始的下标对应每个bit。

2.1 常用命令

setbit

设置bitmap中的值,指定offset,即下标,从0开始。

格式如:setbit <key> <offset> <value>

使用如下,记录学生每天的签到记录,签到则为1,没签到则为0:

122.112.181.245:0>setbit student:20220526 10 1
"0"
122.112.181.245:0>setbit student:20220526 11 1
"0"
122.112.181.245:0>setbit student:20220526 8 1
"0"
复制代码

查看结果如下,以0开始,从左向右分别能看到8,10,11被设置为1:

getbit

获取bitmap的值,指定offset下标,有则返回1,没有则返回0,不存在的下标也返回0。

格式如:get <key> <offset>

使用如下:

122.112.181.245:0>getbit student:20220526 10
"1"
122.112.181.245:0>getbit student:20220526 11
"1"
122.112.181.245:0>getbit student:20220526 12
"0"
复制代码

bitcout

统计设置为1的bit数量,可以指定获取的范围。

格式为:bitcount <key> [start end]

需要注意的是:start 和 end 是按照字节来进行统计的,我们前面放入的 8 ,10 ,11,三个学生的记录,总共占据16位,也就是两个字节,而刚好前八位没有数据:

当我们进行测试的时候就会有如下的现象:

122.112.181.245:0>bitcount student:20220526 0 0
"0"
122.112.181.245:0>bitcount student:20220526 0 1
"3"
复制代码

当我们使用范围 0 ~ 0 时,实际获取的是第一个字节的统计数,显然是0。

而当我们使用 0 ~ 1 时,统计的是这两个字节的值,一共是三个。

不要认为 start 和 end 是对应的bitmap的下标

当我们使用 0 ~ -1 时,表示第一个字节到最后一个字节

当我们使用 0 ~ -2 时,表示第一个字节到倒数第二个字节,我们这只有两个字节,所以相当于 0 ~ 0 ,所以统计不到,如下所示:

122.112.181.245:0>bitcount student:20220526 0 -1
"3"
122.112.181.245:0>bitcount student:20220526 0 -2
"0"
复制代码

关于bitcount,需要好好理解一下,如果使用java的redis客户端,如RedisTemplate等,将这一层原理变相的隐藏掉了,使我们不能直观的感受到 Byte 和 bit 的关系。

bitop

bitop可以进行多种操作是一个复合操作, 它可以对多个Bitmaps做and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 等操作,将结果保存在目标destkey中。

格式如下: bitop <operation> <destkey> <keys...>,其中operation有and,or,not,xor几种。

使用如下,首先创建两个bitmaps:

122.112.181.245:0>setbit users:20220525 1 1
"0"
122.112.181.245:0>setbit users:20220525 3 1
"0"
122.112.181.245:0>setbit users:20220525 5 1
"0"
122.112.181.245:0>setbit users:20220525 7 1
"0"
122.112.181.245:0>setbit users:20220525 9 1
"0"
复制代码
122.112.181.245:0>setbit users:20220526 1 1
"0"
122.112.181.245:0>setbit users:20220526 2 1
"0"
122.112.181.245:0>setbit users:20220526 5 1
"0"
122.112.181.245:0>setbit users:20220526 6 1
"0"
复制代码

做交集,并存入destkey中:users:20220525:and:20220526:

122.112.181.245:0>bitop and users:20220525:and:20220526 users:20220525 users:20220526
"2"
复制代码

结果如下,只有1和5符合:

做并集,并存入destkey中:users:20220525:or:20220526:

122.112.181.245:0>bitop or users:20220525:or:20220526 users:20220525 users:20220526
"2"
复制代码

结果如下:

关于差集和异或,不做单独演示了,使用方式一样。

三、注意

需要注意一点,所说Bitmaps能够节省内存,但是并不适用所有情况。

假设网站有1000万用户,但是其中的活跃用户只有10万。此时Bitmaps存储了绝大多数的僵尸用户,但是bit位的值都是0,是无效的,只有百分之一的利用率,还是浪费了绝大部分的内存。

而如果使用map或者set存储这10万用户,可能还用不了这么多内存呢。

另外还有一种情况,向前面统计的日活数,如果用户的id是这样的:1000001、1000002、1000003等,前面存在100000这样的固定值,那么需要在存储bitmaps进行截取,以免造成内存浪费。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐