本文将详细解释redis中位图的含义。边肖觉得很实用,分享给大家参考。希望你看完这篇文章能有所收获。
00-1010位图,即由大量位组成的数据结构(每个位只能是0和1),主要适用于某些场景下节省空间和有意义地记录数据。
比如大量bool类型的访问,一个用户365天的签到记录,签到是1,没有签到是0。如果使用普通的键/值进行存储,当用户数量较大时,所需的存储空间非常大。
如果用位图存储,一年365天可以存储365位,365位转换成46字节(略长的字符串),节省了大量存储空间。
位图的本质其实就是一个普通的字符串,也就是字节数组。您可以使用get/set直接获取和设置整个位图的内容,也可以使用getbit/setbit将字节数组视为位数组。【相关推荐:Redis视频教程】
00-1010正常设置字符串全部使用set命令。接下来,让我们使用setbit来设置低位数组,最后以获取字符串的形式获取它。
首先,让我们得到两个ASCII码H和E,它们用二进制表示如下。
我们可以看到H的二进制码是01101000,E的二进制码是01100101。我们只需要注意bit为1的位置,然后设置bit。
需要注意的是,位数组的顺序与字符的顺序相反。根据这个原理,我们计算出H字符的每个1的位置是1/2/4,E字符的位置是9/10/13/15。
因此,我们将使用setbit设置一个位数组,并在每个位置(1/2/4/9/10/13/15)设置相应的1。
setbitdata11
setbitdata21
setbitdata41
setbitdata91
setbitdata101
setbitdata131
setbitdata 151零存整取
最后直接拿到数据的钥匙,你会发现你刚拿到他,
setbit get的组合称为零存储整数提取。零存储是逐位设置。整数提取是直接通过键名获取所有数据。
同样,我们也可以进行零存零取、整存零取,其中整存是直接用字符串设置整位数组,零取是通过位的位置来获取位。
零存零取
如您所见,根据setbit,我们用名为w的键设置位数组,只有1/2/4的值设置为1。下图中有getbit w 3获取第三个位置的值,此时默认为0。如果从业务角度触发,可以理解为有4天的签到,但第三天没有签到。
整存零取
如下午所示,我们直接为W的这个键设置一个H字符,然后通过getbit获取W的位数组中的每一位。我们可以看到,得到的内容与上面H字符的二进制内容相同,1/2/4的位置为1,其余为0。
yisu.com/upload/information/20211216/88/87203.jpg" alt="redis中的位图是什么意思">
注意
-
redis的位数组是自动扩充的,如果设置的某个偏移位置超出了现有的内容范围,就会自动将位数组进行零扩充,即扩容的位默认都是0值。
-
如果对应位的字节是不可打印字符,redis-cli将会显示该字符的十六进制形式。
-
一个字节是8个bit(位),要区分字节和位。
统计和查找 (bitcount/bitpos)
redis提供了 统计指令 bitcount 和 位图查找指令 bitpos ,
bitcount用来统计指定位置范围内1的个数,bitpos用来查找指定范围内出现的第一个0或1。
我们可以通过bitcount统计用户一共签到了多少天,通过bitpos指令查找用户从哪一天开始第一次签到,
如果指定了范围参数[start, end],就可以统计在某个时间范围内用户签到了多少天以及用户自某天以后的哪天开始签到,
但是需要注意的是,start和end参数是字节索引,也就是说,指定的位范围必须是8的倍数,
而不能任意指定,所以我们无法直接计算某个月内用户签到了多少天,如果需要计算的话,
可以使用getrange命令取出该月覆盖的字节内容,然后在内存中进行统计,例如2月覆盖了10-12个字节,就使用 getrange w 8 12 。
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitcount w # 所有字符中有多少个1 (integer) 21 127.0.0.1:6379> bitcount w 0 0 # 第一个字符中 1 的位数 (integer) 3 127.0.0.1:6379> bitcount w 0 1 # 前两个字符中 1 的位数 (integer) 7 127.0.0.1:6379> bitpos w 0 # 第一个 0 位 (integer) 0 127.0.0.1:6379> bitpos w 1 # 第一个 1 位 (integer) 1 127.0.0.1:6379> bitpos w 1 1 1 # 从第二个字符算起,第一个1位 (integer) 9 127.0.0.1:6379> bitpos w 1 2 2 # 从第三个字符算起,第一个1位 (integer) 17
bitfield
之前介绍的 setbit / getbit 指定位的值都是单个位,如果要一次操作多个位,就必须使用管道来处理,
在redis3.2以后,提供了bitfield指令,可以一次对多个位进行操作,bitfield有三个子指令,分别是get/set/incrby, 都可以对指定位片段进行读写,
但是最多只能处理64个连续的位,如果超过64位,就需要使用多个子指令,bitfield可以一次执行多个子指令。
示例
下面对下图的位数组使用 bitfield 做一些操作
127.0.0.1:6379> bitfield w get u4 0 # 从第1个位开始取4个位,结果是无符号数(u) 1) (integer) 6 127.0.0.1:6379> bitfield w get u3 2 # 从第3个位开始取3个位,结果是无符号数(u) 1) (integer) 5 127.0.0.1:6379> bitfield w get i4 0 # 从第1个位开始取4个位,结果是有符号数(i) 1) (integer) 6 127.0.0.1:6379> bitfield w get i3 2 # 从第3个位开始取3个位,结果是有符号数(i) 1) (integer) -3
有符号数是指获取的位数组中的第一个位是符号位,剩下的才是值,如果第一位是1,就是负数,
无符号数表示非负数,没有符号位,获取的位数组全部都是值,有符号数最多可以获取64位,
无符号数只能获取63位,因为redis协议中的integer是有符号数,最大64位,不能传递64位的无符号值,
如果超出位数限制,redis就会告诉你参数错误。
上面的指令可以合并成一条指令,可以看到得到的结果是一样的,
bitfield w get u4 0 get u3 2 get i4 0 get i3 2
set修改
我们从第9个位开始,用8个无符号数替换已经存在的8个位,其实就是把第二个字符替换了,由e变成a(它的ASCII码是97),可以看到结果也变成了 hallo
127.0.0.1:6379> bitfield w set u8 8 97 1) (integer) 101 127.0.0.1:6379> get w "hallo"
incrby
incrby对指定范围的位进行自增操作,即++,这可能会发生溢出,如果增加了正数,会出现上溢出,如果增加的是负数,会出现下溢出,
redis默认的处理是折返,即如果出现了溢出,就将溢出的符号位丢掉,例如,如果是8位无符号数255,加1后就全部变成0,如果是8位有符号数127,加1后就溢出变成-128。
依然根据hello字符,来演示一下 incrby
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w get u4 2 # 从第3位开始取4个无符号整数,第一次是10 1) (integer) 10 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w incrby u4 2 1 #到这里的时候,已经溢出折返成0了 1) (integer) 0
bitfield指令提供溢出策略子指令 overflow,用户可以选择溢出行为,默认是折返(wrap),还可以选择失败(fail) 即报错不执行,还有饱和截断(sat) 即超过范围就停留在最大或最小值,
overflow指令只影响接下来的第一条指令,这条指令执行完以后,溢出策略就会变成默认值 折返(wrap)。
饱和截断
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 # 接下来的都将是保持最大值 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15
失败不执行
127.0.0.1:6379> set w hello OK 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 11 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 12 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 13 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 14 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 15 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 # 接下来的都是失败 1) (nil) 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (nil) 127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (nil)
get/set/incrby一起执行
127.0.0.1:6379> bitfield w set u4 1 0 get u4 1 incrby u4 2 1 1) (integer) 0 2) (integer) 0 3) (integer) 1 127.0.0.1:6379> get w "\x04ello"
关于“redis中的位图是什么意思”这篇文章就分享到这里了,希望
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/148260.html