Redis高级数据结构——位图

 对于bool型数据,存取时使用普通的key/value会占很大的内存,可以使用位图来存放,可以大大节省存储空间。

 比如用户的签到情况,1表示已签到,0表示未签到,那么一年的签到情况只需要使用一个365位(46个字节)的空间即可存储。若是使用字符串的话则需要365个字节,当用户数量庞大的时候,使用位图是非常节省空间的。

 位图不是特殊的数据结构,其实就只是一个byte数组,如下图所示。

redis可以存储图片么 redis可以存图片吗_bit

1.位图的基本操作

setbit key offset value #将位图中的offset位设为value

getbit key offset #得到位图中offset位的值

(1)零存整取

 所谓“零存整取”就是,使用setbit对位值进行逐个设置,再将整个字符串取出。

下面使用位操作将字符串设置为“hello”。

 首先,需要得到hello的ASCII码,用python命令 bin(ord())获得

redis可以存储图片么 redis可以存图片吗_bitmap_02


 接下来使用位操作进行设置得到“hello”,这里只需要设置值为1的位,因为位数组默认为全0的,且redis的位数组是自动扩展的,扩充时会自动将数组进行0扩充。

redis可以存储图片么 redis可以存图片吗_bitmap_03


redis可以存储图片么 redis可以存图片吗_bitmap_04


 经过对图中值为1的位置进行置1之后,得到了"hello"。(2)零存零取

 使用单个位操作设置位值,使用单个位操作获取具体位值

 获取上面"hello"的4, 11, 34位

redis可以存储图片么 redis可以存图片吗_bit_05


redis可以存储图片么 redis可以存图片吗_bit_06


 获取的是图中蓝色块的值。(3)整存零取

 使用字符串操作批量设置位值,使用单个位操作获取具体位值

redis可以存储图片么 redis可以存图片吗_bitmap_07


(2)位图的统计和查找

bitcount key [start end] #统计指定范围内1的个数

bitpos key bit [start end] #查找指定范围内出现的第一个0或1

 注意这里的指定范围不是按位来算的,按的是字符串字符数组的位置

redis可以存储图片么 redis可以存图片吗_位图_08


3.多个位的操作

 redis在3.2版本以后新增了一个指令bitfield,有了这个指令,可以不用管道也可以一次进行多个位的操作。

bitfield key get type offset #从指定位置开始取多个位

 从0号位开始,取4个位,结果是无符号数(u)

 取得结果为6,二进制为0110

redis可以存储图片么 redis可以存图片吗_redis_09


redis可以存储图片么 redis可以存图片吗_位图_10

 从2号位开始,取3个位,结果是有符号数(i)。

 取得结果为-3,二进制为1111 1101,由于是有符号数,则前面全部补1。

redis可以存储图片么 redis可以存图片吗_redis可以存储图片么_11


redis可以存储图片么 redis可以存图片吗_位图_12


 可以一次执行多个子指令

redis可以存储图片么 redis可以存图片吗_bit_13


redis可以存储图片么 redis可以存图片吗_bit_14

bitfield key set type offset value #从指定位置开始设置多个位的值
从8号位开始,接连8个位都用无符号数97替换

redis可以存储图片么 redis可以存图片吗_bit_15


 可以看到这里e被替换为a了

bitfield key incrby type offset increment #从指定位置开始对多个位进行自增

 从2号位开始,对接连4个位构成的无符号数+2

 得到12,即为1010+0010 = 1100,为12的二进制

 此时h变为了p,p的二进制为0111 0000

redis可以存储图片么 redis可以存图片吗_bit_16


redis可以存储图片么 redis可以存图片吗_bitmap_17


 接着我们继续对这4位构成的无符号数+2,发现变0了

redis可以存储图片么 redis可以存图片吗_位图_18


 此时这段的值依次变为

 1100 + 0010 = 1110

 1110 + 0010 = 10000

 发现此时出现了溢出,因而变为了0

 redis默认的处理溢出是折返,如果出现了溢出,就将溢出的符号位丢掉。

 其实bitfield指令提供了溢出策略子指令overflow,用户可以选择溢出行为,默认为折返,还可以选择失败不执行,或者饱和截断。
 overflow指令值影响接下来的第一条指令,这条指令执行完后溢出策略会变成默认值折返。

失败不执行(fail)

 报错不执行此次增加

redis可以存储图片么 redis可以存图片吗_redis可以存储图片么_19


饱和截断(sat)

 超过了范围就停留在最大值或最小值上

redis可以存储图片么 redis可以存图片吗_redis_20


 当然bitfield还可以同时执行这三种子指令

redis可以存储图片么 redis可以存图片吗_redis可以存储图片么_21

 字符串变为下图所示

redis可以存储图片么 redis可以存图片吗_bitmap_22