Redis中的位图
Redis中的位图
Redis中的位图是由多个二进制位组成的数组,数组中每个二进制位都有与之对应的偏移量(索引),用户通过索引可以对位图中制定的一个或者多个二进制位进行操作。
通过位图我们可以:
- 为位图指定偏移量上的二进制位设置值,或者获取位图指定偏移量上的二进制位的值。
- 统计位图中有多少二进制位被设置成了1。
- 查找位图中第一个被设置为指定值的二进制位并返回它的偏移量。
- 对一个或者多个位图执行逻辑并、逻辑或、逻辑异或以及逻辑非运算。
- 将指定类型的整数存储到位图中。
1. SETBIT:设置二进制位的值
SETBIT bitmap offset value
SETBIT命令在对二进制位进行设置之后,将返回二进制位被设置之前的旧值作为结果。当用户所设置的位图不存在或者位图当前的大小无法满足用户想要执行的设置操作,那么Redis将对被设置的位图进行扩展满足设置要求。
因为Redis对位图的扩展操作是以字节为单位进行的,所以扩展之后的位图包含的二进制位数量可能会比命令中要求的多,并且在扩展位图的同时,Redis还会将所有未被设置的二进制位的值初始化为0。
SETBIT命令只能使用正数偏移量,尝试使用负数将会引发错误。
其他信息
-
复杂度:O(1)。
-
版本要求:SETBIT命令从Redis 2.2.0版本开始可用。
2. GETBIT:获取二进制位的值
GETBIT bitmap offset
GETBIT命令可以获取位图指定偏移量的值,并且也只接受正数作为偏移量。如果用户输入的偏移量超过了位图目前拥有的最大偏移量,那么该命令将返回0作为结果
其他信息
-
复杂度:O(1)。
-
版本要求:GETBIT命令从Redis 2.2.0版本开始可用。
3. BITCOUNT:统计被设置的二进制位数量
BITCOUNT bitmap [start end]
用户可以执行BITCOUNT命令统计位图中值为1的二进制位数量,用户也可以通过可选的start参数和end参数,让BITCOUNR只对指定字节范围内对二进制位进行统计。
这里需要注意start和end参数是用来指定偏移量而不是二进制位偏移量的。这些参数还可以是负数索引。
其他信息
-
复杂度:O(N),其中N为被统计字节的数量。
-
版本要求:BITCOUNT命令从Redis 2.6.0版本开始可用。
4. BITPOS:查找第一个指定的二进制位值
BITPOS bitmap value [start end]
用户可以使用该命令,在位图中查找第一个被设置为指定值的二进制位,并返回这个二进制位的偏移量。也可以在指定的字节返回内查找。同样可以使用负数索引。
当用户尝试对一个不存在的位图或者一个所有位都设置成了0的位图中查找值为1 的二进制位时,BITPOS命令将返回-1作为结果。
如果用户在一个所有位都被设置成1的位图中查找值为0的二进制位,那么BITPOS命令将返回位图最大偏移量加上1作为结果。
其他信息
-
复杂度:O(N),其中N为查找涉及的字节数量。
-
版本要求:BITPOS命令从Redis 2.8.7版本开始可用。
5. BITOP:执行二进制位运算
BITOP operation result_key bitmap [bitmap ...]
用户可以同过该命令,对一个或者多个位图执行指定的二进制位运算,并将运算结果存储到指定的键中,operation参数的值可以是AND、OR、XOR、NOT中的任意一个,当BITOP命令在对两个不同长度的位图执行运算时,会将长度较短的那个位图中不存在的二进制位的值看作0。
其他信息
-
复杂度:O(N),其中N为计算涉及的字节总数量。
-
版本要求:BITOP命令从Redis 2.6.0版本开始可用。
6. BITFIELD:在位图中存储整数值
“在一般情况下,当用户使用字符串或者散列去存储整数的时候,Redis都会为被存储的整数分配一个long类型的值(通常为32位长或者64位长),并使用对象去包裹这个值,然后再把对象关联到数据库或者散列中。
与此相反,BITFIELD命令允许用户自行指定被存储整数的类型,并且不会使用对象去包裹这些整数,因此当我们想要存储长度比long类型短的整数,并且希望尽可能地减少对象包裹带来的内存消耗时,就可以考虑使用位图来存储整数。”
BITFIELD命令允许用户在位图中的任意区域field存储指定长度的整数值,并对这些整数值执行加法或者减法操作。
1. 根据偏移量对区域进行设置
通过使用BITFIELD命令的SET子命令,用户可以在位图的指定偏移量offset(标识第n个二进制位,从0开始)上设置一个type类型的整数值vlaue:
BITFIELD bitmap SET type offset value
- type参数用于指定被设置值的类型,这个参数的值需要以i或者u为前缀,后跟被设置值的位长度,其中i表示被设置的值为有符号整数,而u则表示被设置的值为无符号整数。比如i8表示被设置的值为有符号8位整数,而u16则表示被设置的值为无符号16位整数,诸如此类。BITFIELD的各个子命令目前最大能够对64位长的有符号整数(i64)和63位长的无符号整数(u63)进行操作。
- value参数用于指定被设置的整数值,这个值的类型应该和type参数指定的类型一致。如果给定值的长度超过了type参数指定的类型,那么SET命令将根据type参数指定的类型截断给定值。比如,如果用户尝试将整数123(二进制表示为01111011)存储到一个u4类型的区域中,那么命令会先将该值截断为4位长的二进制数字1011(即十进制数字11),然后再进行设置。
2. 根据索引对区域进行设置
SET子命令还允许用户根据给定类型的位长度,对位图在指定索引上存储的整数值进行设置:
BITFIELD bitmap SET type #index value
当位图中存储的都是相同类型的整数值时,使用这种设置方法将给用户带来非常大的便利,因为这种方法允许用户直接对位图指定索引上的整数值进行设置,而不必知道这个整数值具体存储在位图的哪个偏移量上。
假设现在有一个位图,它存储着多个8位长的无符号整数,而我们想要把它的第133个8位无符号整数的值设置为22。如果使用SET子命令的偏移量设置格式,就需要先使用算式(133-1)*8计算出第133个8位无符号整数在位图中的起始偏移量1056,然后再执行以下命令:
BITFIELD bitmap SET u8 1056 22
很明显,这种手动计算偏移量然后进行设置的做法非常麻烦也很容易出错。与此相反,如果我们使用的是SET子命令的索引设置格式,那么只需要执行以下命令就可以对位图的第133个无符号整数进行设置了。
3. 获取区域存储的值
通过GET子命令,用户可以从给定偏移量或者索引中取出指定类型的整数值:
BITFIELD bitmap GET type offset
BITFIELD birmap GET type #index
各个参数与SET命令基本一致,当给定的索引或者偏移量超出了位图的边界,那么GET子命令将返回0作为结果。
4. 执行加法操作活着减法操作
通过INCRBY子命令,可以实现对位图存储的整数值执行加法操作或者减法操作。通过传入对应的负数增量来达到执行减法操作的效果。
BITFIBLD bitmap INCEBY type offset increment
BITFIBLD bitmap INCEBY type #index increment
5. 处理溢出
使用OVERFLOW子命令去控制INCRBY子命令在计算时发生溢出的行为。
BITFIBLD bitmap [...] OVERFLOW WROP|SAT|FAIL [...]
WRAP表示使用回绕(wrap around)方式处理溢出,这也是C语言默认的溢出处理方式。在这一模式下,向上溢出的整数值将从类型的最小值开始重新计算,而向下溢出的整数值则会从类型的最大值开始重新计算。
SAT表示使用饱和运算(saturation arithmetic)方式处理溢出,在这一模式下,向上溢出的整数值将被设置为类型的最大值,而向下溢出的整数值则会被设置为类型的最小值。
FAIL表示让INCRBY子命令在检测到计算会引发溢出时拒绝执行计算,并返回空值表示计算失败.
OVERFLOW子命令在执行时将不产生任何回复。此外,如果用户在执行BITFIELD命令时没有指定具体的溢出处理方式,那么INCRBY子命令默认使用WRAP方式处理计算溢出。
OVERFLOW子命令只会对同一个BITFIELD调用中排在它之后的那些INCRBY子命令产生效果,所以用户必须把OVERFLOW子命令放到它想要影响的INCRBY子命令之前。
其他信息
-
复杂度:O(N),其中N为用户给定的子命令数量。
-
版本要求:BITFIELD命令从Redis 3.2.0版本开始可用。
总结
-
Redis的位图是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
-
BITCOUNT命令接受的是字节索引范围,而不是二进制位索引范围,忽略这一点很容易引发程序错误。
-
BITFIELD命令允许用户自行指定被存储整数的类型,并且不会使用对象去包裹这些整数,因此当我们想要存储长度比long类型短的整数,并且希望尽可能地减少对象包裹带来的内存消耗时,就可以考虑使用位图来存储整数。
-
因为位图是使用字符串实现的,所以字符串命令也可以用于处理位图命令。但是在使用字符串命令操作位图时,用户必须先把命令返回的字符串值转换成二进制值,然后再进行后续处理。