数据结构之位图
Redis的位图(bitmap)是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
SETBIT:设置二进制位的值
SETBIT bitmap offset value
如果我们想要将位图bitmap001的值设置成10010100,那么可以执行以下3个命令:
redis> SETBIT bitmap001 0 1
(integer) 0 -- 二进制位原来的值为0
redis> SETBIT bitmap001 3 1
(integer) 0
redis> SETBIT bitmap001 5 1
(integer) 0
当用户执行SETBIT命令尝试对一个位图进行设置的时候,如果位图不存在,或者位图当前的大小无法满足用户想要执行的设置操作,那么Redis将对被设置的位图进行扩展,使得位图可以满足用户的设置请求。
与一些Redis命令可以使用负数作为偏移量的做法不同,SETBIT命令只能使用正数偏移量。
GETBIT:获取二进制位的值
BITCOUNT:统计被设置的二进制位数量
用户可以通过执行BITCOUNT命令统计位图中值为1的二进制位数量。
在有需要的情况下,用户也可以通过可选的start参数和end参数,让BITCOUNT只对指定字节范围内的二进制位进行统计:
BITCOUNT bitmap [start end]
注意start参数和end参数与本章之前介绍的SETBIT命令和GETBIT命令的offset参数并不相同,这两个参数是用来指定字节偏移量而不是二进制位偏移量的。
BITCOUNT命令的start参数和end参数的值除了可以是正数之外,还可以是负数。
BITPOS:查找第一个指定的二进制位值
BITOP:执行二进制位运算
BITOP operation result_key bitmap [bitmap ...]
operation参数的值可以是AND、OR、XOR、NOT中的任意一个,这4个值分别对应逻辑并、逻辑或、逻辑异或和逻辑非4种运算,其中AND、OR、XOR这3种运算允许用户使用任意数量的位图作为输入,而NOT运算只允许使用一个位图作为输入。BITOP命令在将计算结果存储到指定键中之后,会返回被存储位图的字节长度。
当BITOP命令在对两个长度不同的位图执行运算时,会将长度较短的那个位图中不存在的二进制位的值看作0。
BITFIELD:在位图中存储整数值
BITFIELD命令允许用户在位图中的任意区域(field)存储指定长度的整数值,并对这些整数值执行加法或减法操作。
BITFIELD命令支持SET、GET、INCRBY、OVERFLOW这4个子命令。
BITFIELD bitmap SET type offset value
·offset参数用于指定设置的起始偏移量。
·type参数用于指定被设置值的类型,这个参数的值需要以i或者u为前缀,后跟被设置值的位长度,其中i表示被设置的值为有符号整数,而u则表示被设置的值为无符号整数。比如i8表示被设置的值为有符号8位整数。
·value参数用于指定被设置的整数值,这个值的类型应该和type参数指定的类型一致。
BITFIELD命令除了可以使用INCRBY子命令来执行加法操作和减法操作之外,还可以使用OVERFLOW子命令去控制INCRBY子命令在发生计算溢出时的行为:
BITFIELD bitmap [...] OVERFLOW WRAP|SAT|FAIL [...]
OVERFLOW子命令的参数可以是WRAP、SAT或者FAIL中的一个:
WRAP表示使用回绕(wrap around)方式处理溢出,这也是C语言默认的溢出处理方式。在这一模式下,向上溢出的整数值将从类型的最小值开始重新计算,而向下溢出的整数值则会从类型的最大值开始重新计算。(例如u类型最小值0.max 3 2+2=0)
SAT表示使用饱和运算(saturation arithmetic)方式处理溢出,在这一模式下,向上溢出的整数值将被设置为类型的最大值,而向下溢出的整数值则会被设置为类型的最小值。
FAIL表示让INCRBY子命令在检测到计算会引发溢出时拒绝执行计算,并返回空值表示计算失败。
如果用户在执行BITFIELD命令时没有指定具体的溢出处理方式,那么INCRBY子命令默认使用WRAP方式处理计算溢出。
OVERFLOW子命令只会对同一个BITFIELD调用中排在它之后的那些INCRBY子命令产生效果,所以用户必须把OVERFLOW子命令放到它想要影响的INCRBY子命令之前。
使用字符串命令对位图进行操作
因为Redis的位图是在字符串的基础上实现的,所以它会把位图键看作一个字符串键。
当我们使用字符串命令获取位图的值时,命令返回的是一个字符串,而不是一个二进制形式的位图:比如GET命令返回的就是字符串"\x04"而不是二进制位图00000100。因此我们在使用字符串命令操作位图的时候,必须先将命令返回的字符串转换回二进制形式,然后再执行具体的二进制操作。