Redis高级数据结构——位图
对于bool型数据,存取时使用普通的key/value会占很大的内存,可以使用位图来存放,可以大大节省存储空间。
比如用户的签到情况,1表示已签到,0表示未签到,那么一年的签到情况只需要使用一个365位(46个字节)的空间即可存储。若是使用字符串的话则需要365个字节,当用户数量庞大的时候,使用位图是非常节省空间的。
位图不是特殊的数据结构,其实就只是一个byte数组,如下图所示。
1.位图的基本操作
setbit key offset value #将位图中的offset位设为value
getbit key offset #得到位图中offset位的值
(1)零存整取
所谓“零存整取”就是,使用setbit对位值进行逐个设置,再将整个字符串取出。
下面使用位操作将字符串设置为“hello”。
首先,需要得到hello的ASCII码,用python命令 bin(ord())获得
接下来使用位操作进行设置得到“hello”,这里只需要设置值为1的位,因为位数组默认为全0的,且redis的位数组是自动扩展的,扩充时会自动将数组进行0扩充。
经过对图中值为1的位置进行置1之后,得到了"hello"。
(2)零存零取
使用单个位操作设置位值,使用单个位操作获取具体位值
获取上面"hello"的4, 11, 34位
获取的是图中蓝色块的值。
(3)整存零取
使用字符串操作批量设置位值,使用单个位操作获取具体位值
(2)位图的统计和查找
bitcount key [start end] #统计指定范围内1的个数
bitpos key bit [start end] #查找指定范围内出现的第一个0或1
注意这里的指定范围不是按位来算的,按的是字符串字符数组的位置
3.多个位的操作
redis在3.2版本以后新增了一个指令bitfield,有了这个指令,可以不用管道也可以一次进行多个位的操作。
bitfield key get type offset #从指定位置开始取多个位
从0号位开始,取4个位,结果是无符号数(u)
取得结果为6,二进制为0110
从2号位开始,取3个位,结果是有符号数(i)。
取得结果为-3,二进制为1111 1101,由于是有符号数,则前面全部补1。
可以一次执行多个子指令
bitfield key set type offset value #从指定位置开始设置多个位的值
从8号位开始,接连8个位都用无符号数97替换
可以看到这里e被替换为a了
bitfield key incrby type offset increment #从指定位置开始对多个位进行自增
从2号位开始,对接连4个位构成的无符号数+2
得到12,即为1010+0010 = 1100,为12的二进制
此时h变为了p,p的二进制为0111 0000
接着我们继续对这4位构成的无符号数+2,发现变0了
此时这段的值依次变为
1100 + 0010 = 1110
1110 + 0010 = 10000
发现此时出现了溢出,因而变为了0
redis默认的处理溢出是折返,如果出现了溢出,就将溢出的符号位丢掉。
其实bitfield指令提供了溢出策略子指令overflow,用户可以选择溢出行为,默认为折返,还可以选择失败不执行,或者饱和截断。
overflow指令值影响接下来的第一条指令,这条指令执行完后溢出策略会变成默认值折返。
失败不执行(fail)
报错不执行此次增加
饱和截断(sat)
超过了范围就停留在最大值或最小值上
当然bitfield还可以同时执行这三种子指令
字符串变为下图所示