16. Redis Bitmaps位图(SETBIT、GETBIT、BITOP、BITCOUNT、BITPOS、BITFIELD)

目录


Redis专栏目录(点击进入…)



十年前你是谁,一年前你是谁,甚至昨天你是谁,都不重要。
重要的是,
今天你是谁,以及明天你将成为谁。
希望你一如既往地坚强勇敢,
站在迎着光的地方,
活成自己想要的模样。

快来加入我们的互动学习群吧,与我们一起成长和进步吧! QQ交流群:948912943

Bitmaps(位图)

Redis的位图(bitmap)是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。

Bitmaps不是实际的数据类型,而是在String类型上定义的一组面向位的操作。在 Redis 中,Bitmaps 是一种特殊的数据结构,用于处理位图信息。由于字符串是二进制安全Blob,其最大长度为512 MB,因此适合设置多达2^32个不同的位。对于一些特定的场景,比如权限控制、用户在线状态等,是非常有用的。

下图展示了一个包含8个二进制位的位图示例,这个位图存储的值为01010000。
在这里插入图片描述

位操作分为两组:
①恒定时间单比特操作,如将位设置为1或0,或获取其值。
②对位组的操作,如计数给定位范围内的设置位的数量(如总体计数)。

Bitmaps最大的优点之一:在存储信息时,它们通常可以极大地节省空间
例如:在不同用户由增量用户ID表示的系统中,仅使用512MB的内存就可以记住40亿用户的单比特信息(例如,知道用户是否想接收时事通讯)。

使用SETBIT和GETBIT命令设置和检索位:

> setbit key 10 1
(integer) 1
> getbit key 10
(integer) 1
> getbit key 11
(integer) 0
命令描述
SETBIT将提供的偏移量处的一位设置为 0 或 1。
第一个自变量是比特数,第二个自变量是要将比特设置为的值,即1或0。如果寻址位在当前字符串长度之外,则该命令会自动放大字符串。
GETBIT返回给定偏移处的位值。
只返回指定索引处的位的值。超出范围的位(寻址存储在目标密钥中的字符串长度之外的位)总是被认为是零。
BITOP允许对一个或多个字符串执行按位运算。
在不同的字符串之间执行逐位操作。提供的操作有AND、OR、XOR和NOT。
BITCOUNT执行总体计数,报告设置为1的位数。
BITPOS找到具有指定值0或1的第一个比特。

Bitmaps常见应用场景

(1)各种实时分析
(2)存储与对象ID相关联的节省空间但高性能的布尔信息
(3)对于集合成员对应于整数 0-N 的情况,有效的集合表示
(4)对象权限,其中每一位代表一个特定的权限,类似于文件系统存储权限的方式

例如,想知道网站用户每天访问次数最长的一次。从零开始计算天数,也就是公开网站的那一天,并在每次用户访问网站时使用SETBIT设置一点。作为一个位索引,只需取当前unix时间,减去初始偏移量,然后除以3600*24。

这样,对于每个用户,都有一个小字符串,其中包含每天的访问信息。有了BITCOUNT,就可以轻松地获得给定用户访问网站的天数,而通过一些BITPOS调用,或者简单地获取和分析位图客户端,就可以容易地计算最长的连续时间。

Bitmaps拆分为多个键是微不足道的,例如:为了分割数据集,而且通常最好避免。

(1)SETBIT:设置二进制位的值

SETBIT用于设置位图中指定偏移位置的位的值。

时间复杂度:O(1)

语法:

SETBIT key offset value

SETBIT命令在对二进制位进行设置之后,将返回二进制位被设置之前的旧值作为结果。

参数描述
key指定位图的键名
offset指定要设置的位的偏移位置
value指定要设置的位的值,可以是 0 或 1

示例:

# 将位图 key 的第 5 位设置为 1
127.0.0.1:6379> SETBIT testbitmaps 5 1
(integer) 0

以上命令表示:
在这里插入图片描述

位图扩展
当用户执行SETBIT命令尝试对一个位图进行设置的时候,如果位图不存在,或者位图当前的大小无法满足用户想要执行的设置操作,那么Redis将对被设置的位图进行扩展,使得位图可以满足用户的设置请求。

因为Redis对位图的扩展操作是以字节为单位进行的,所以扩展之后的位图包含的二进制位数量可能会比用户要求的稍微多一些,并且在扩展位图的同时,Redis还会将所有未被设置的二进制位的值初始化为0。

偏移量只能为正数
与一些Redis命令可以使用负数作为偏移量的做法不同,SETBIT命令只能使用正数偏移量,尝试输入负数作为偏移量将引发一个错误。


(2)GETBIT:获取二进制位的值

GETBIT 用于获取位图中指定偏移位置的位的值。

时间复杂度:O(1)

语法:

GETBIT key offset
参数描述
key指定位图的键名
offset指定要获取的位的偏移位置

示例:

# 获取位图 testbitmaps 的第 5 位的值
127.0.0.1:6379> GETBIT testbitmaps 5
(integer) 1

下图展示了GETBIT命令对testbitmaps进行取值的过程
在这里插入图片描述

如果用户输入的偏移量超过了位图目前拥有的最大偏移量,那么GETBIT命令将返回0作为结果。

# 获取位图 testbitmaps 的第 999 位的值
127.0.0.1:6379> GETBIT testbitmaps 999
(integer) 0

(3)BITOP:执行二进制位运算

BITOP 用于对多个位图执行按位操作,并将结果存储在目标位图中。

时间复杂度:O(N),其中N为计算涉及的字节总数量。

语法:

BITOP operation destkey key [key ...]
参数描述
operation指定按位操作的类型,可以是 AND、OR、XOR、NOT
destkey指定目标位图的键名
key指定参与操作的位图键名,可以是一个或多个

operation参数的值可以是AND、OR、XOR、NOT中的任意一个,这4个值分别对应逻辑与、逻辑或、逻辑异或和逻辑非4种运算,其中AND、OR、XOR这3种运算允许用户使用任意数量的位图作为输入,而NOT运算只允许使用一个位图作为输入。BITOP命令在将计算结果存储到指定键中之后,会返回被存储位图的字节长度。

示例:

# 对位图 key1 和 key2 执行按位 OR 操作,将结果存储在 destkey 中
127.0.0.1:6379> BITOP OR destkey key1 key2
(integer) 6

在这个示例中,destkey 存储了执行 OR 操作的结果,其中有 6 个位被设置为 1。

对于位图bitmap001、bitmap002和bitmap003:

bitmap00110010100
bitmap0020000000000100000
bitmap003111011010000110110100110

通过执行以下命令,可以对位图bitmap001、bitmap002和bitmap003执行逻辑与运算(先对bitmap001、bitmap002进行与运算,然后用他们的结果对bitmap003进行与运算,后同),并将结果存储到键bitop_result中:

127.0.0.1:6379> BITOP AND bitop_result bitmap001 bitmap002 bitmap003
(integer) 6  => 与运算结果bitop_result位图的长度为3个字节

bitop_result:000000000000000000000000

而执行以下命令则可以将bitmap001、bitmap002和bitmap003的逻辑或运算结果和逻辑异或运算结果分别存储到指定的键中:

127.0.0.1:6379> BITOP OR or_result bitmap001 bitmap002 bitmap003
(integer) 3
127.0.0.1:6379> BITOP XOR xor_result bitmap001 bitmap002 bitmap003
(integer) 3

or_result:111111010010110110100110
xor_result:011110010010110110100110

最后,通过执行以下命令,我们可以计算出bitmap001的逻辑非结果,并将它存储到not_result键中:

127.0.0.1:6379> BITOP OR not_result bitmap001
(integer) 1

not_result:01101011

处理不同长度的位图
当BITOP命令在对两个长度不同的位图执行运算时,会将长度较短的那个位图中不存在的二进制位的值看作0。


(4)BITCOUNT:统计被设置的二进制位数量

用户可以通过执行BITCOUNT命令统计位图中值为1的二进制位数量。

时间复杂度:O(N),其中N为被统计字节的数量

语法:

BITCOUNT key [start end]

默认统计整个位图,可选参数 start 和 end 可以指定被统计的字节范围(包含 start 和 end)。

参数描述
key指定参与操作的位图键名,可以是一个或多个
start开始字节索引
end结束字节索引

对于值为00001000的位图testbitmaps ,可以通过执行以下命令来统计它有多少个二进制位被设置成了1:

127.0.0.1:6379> BITCOUNT testbitmaps 
(integer) 1

只统计位图指定字节范围内的二进制位
在默认情况下,BITCOUNT命令将对位图包含的所有字节中的二进制位进行统计,但在有需要的情况下,用户也可以通过可选的start参数和end参数,让BITCOUNT只对指定字节范围内的二进制位进行统计。

注意:start参数和end参数与本章之前介绍的SETBIT命令和GETBIT命令的offset参数并不相同,这两个参数是用来指定字节偏移量而不是二进制位偏移量的。位图的字节偏移量与Redis其他数据结构的偏移量一样,都是从0开始的:位图第一个字节的偏移量为0,第二个字节的偏移量为1,第三个字节的偏移量为2,以此类推。

不要把BITCOUNT的字节偏移量当作二进制位偏移量,start和end为字节偏移量

例子:
在这里插入图片描述

统计出它的第一个字节里面有多少个二进制位被设置成了1:

127.0.0.1:6379> BITCOUNT testbitmaps001 0 0
(integer) 4

在这里插入图片描述


127.0.0.1:6379> BITCOUNT testbitmaps001 1 1
(integer) 3

在这里插入图片描述


127.0.0.1:6379> BITCOUNT testbitmaps001 0 1
(integer) 7

在这里插入图片描述

使用负数偏移量定义统计范围
BITCOUNT命令的start参数和end参数的值除了可以是正数之外,还可以是负数。

负数索引:倒数第一个为-1,倒数第二为-2,以此类推。

以下是一些使用负数偏移量对位图testbitmaps001的指定字节进行统计的例子:

127.0.0.1:6379> BITCOUNT testbitmaps001 -1 -1
(integer) 3

127.0.0.1:6379> BITCOUNT testbitmaps001 -1 -2
(integer) 7

127.0.0.1:6379> BITCOUNT testbitmaps001 -2 -2
(integer) 4

127.0.0.1:6379> BITCOUNT testbitmaps001 0 -1
(integer) 7 (统计整个位图)

(5)BITPOS:查找第一个指定的二进制位值

用户可以通过执行BITPOS命令,在位图中查找第一个被设置为指定值的二进制位(只能是0或者1),并返回这个二进制位的偏移量。

时间复杂度:O(N),其中N为查找涉及的字节数量。

语法:

BITPOS key bit [start] [end]
参数描述
key指定参与操作的位图键名,可以是一个或多个
bit二进制位偏移量
start开始字节索引
end结束字节索引

示例:
对于下面的位图来说:
在这里插入图片描述

比如,通过执行以下命令,可以知道位图testbitmaps001第一个被设置为1的二进制位所在的偏移量:

127.0.0.1:6379> BITPOS testbitmaps001 1
(integer) 1

而执行以下命令则可以知道bitmap003第一个被设置为0的二进制位所在的偏移量:

127.0.0.1:6379> BITPOS testbitmaps001 0
(integer) 0

只在指定的字节范围内进行查找
在默认情况下,BITPOS命令的查找范围将覆盖位图包含的所有二进制位,但在有需要的情况下,用户也可以通过可选的start参数和end参数,让BITPOS命令只在指定字节范围内的二进制位中进行查找。可以参考上面的BITCOUNT。

使用负数偏移量定义查找范围
与BITCOUNT命令一样,BITPOS命令的start参数和end参数也可以是负数

边界情况处理
当用户尝试对一个不存在的位图或者一个所有位都被设置成了0的位图中查找值为1的二进制位时,BITPOS命令将返回-1作为结果。

127.0.0.1:6379> BITPOS non-exists 1
(integer) -1
127.0.0.1:6379> BITPOS all-0-bitmap 1
(integer) -1

如果用户在一个所有位都被设置成1的位图中查找值为0的二进制位,那么BITPOS命令将返回位图最大偏移量加上1作为结果。

举个例子,如果在一个包含8个二进制位,并且所有二进制位都被设置成1的位图bitmap-8bits-all-1中查找值为0的二进制位,那么BITPOS命令将返回以下结果:

127.0.0.1:6379> BITPOS bitmap-8bits-all-1 1
(integer) 8

这个BITPOS命令之所以会返回8,是因为它在对位图中偏移量从0到7的8个二进制位进行检查之后,都没有找到值为0的二进制位,于是它继续移动指针,尝试去检查偏移量为8的二进制位,但是由于偏移量8已经超出了位图的有效偏移量范围,而Redis又会把位图中不存在的二进制位的值看作0,所以BITPOS命令最后就把偏移量8作为结果返回给用户。


(6)BITFIELD:在位图中存储整数值

BITFIELD命令允许用户在位图中的任意区域(field)存储指定长度的整数值,并对这些整数值执行加法或减法操作。BITFIELD命令支持SET、GET、INCRBY、OVERFLOW这4个子命令

语法:

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
参数描述
key指定参与操作的位图键名
offset参数用于指定设置的起始偏移量。
这个偏移量从0开始计算,偏移量为0表示设置从位图的第1个二进制位开始。如果被设置的值长度不止一位,那么设置将自动延伸至之后的二进制位。
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),然后再进行设置。

SET子命令会返回指定区域被设置之前的旧值作为执行结果。

根据偏移量对区域进行设置
通过使用BITFIELD命令的SET子命令,用户可以在位图的指定偏移量offset上设置一个type类型的整数值value:

语法:

BITFIELD key [SET type offset value]

示例:
通过执行以下命令,可以从偏移量0开始,设置一个8位长的无符号整数值188(二进制表示为10111100):

127.0.0.1:6379> BITFIELD bitmap SET u8 0 188
(integer) 0

被设置之前存储的整数值为0。下图展示了执行设置命令之后的位图:
在这里插入图片描述

BITFIELD命令允许用户在一次调用中执行多个子命令,比如,通过在一次BITFIELD调用中使用多个SET子命令,可以同时对位图的多个区域进行设置:

127.0.0.1:6379> BITFIELD bitmap SET u8 0 8 SET i32 20 88 SET i64 188 888
(integer) 0
(integer) 0
(integer) 0

第1个子命令SET u8 0 8从偏移量0开始,设置一个8位长无符号整数值8。
第2个子命令SET i32 20 88从偏移量20开始,设置一个32位长有符号整数值88。
第3个子命令SET i64 188 888从偏移量188开始,设置一个64位长有符号整数值888。

对于这3个子命令,BITFIELD命令返回了一个包含3个元素的数组作为命令的执行结果,这3个元素分别代表3个指定区域被设置之前存储的整数值,比如第一个子命令返回的结果就是之前为该区域设置的值198。下图展示了这个BITFIELD命令创建出的位图以及被设置的3个整数值在位图中所处的位偏移量。
在这里插入图片描述

上图也展示了SET子命令的两个特点:
(1)设置可以在位图的任意偏移量上进行,被设置区域之间不必是连续的,也不需要进行对齐(align)。各个区域之间可以有空洞,即未被设置的二进制位,这些二进制位会自动被初始化为0。
(2)在同一个位图中可以存储多个不同类型和不同长度的整数。

虽然这两个特点可以带来很大的灵活性,但是从节约内存、避免发生错误等情况考虑,一般还是应该:
(1)以对齐的方式使用位图,并且让位图尽可能地紧凑,避免包含过多空洞。
(2)每个位图只存储同一种类型的整数,并使用int-8bit、unsigned-16bit这样的键名前缀来标识位图中存储的整数类型。

根据索引对区域进行设置
除了根据偏移量对位图进行设置之外,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个8位无符号整数进行设置了:

BITFIELD bitmap set u8 #132 22

注意:因为SET子命令接受的索引是从0开始计算的,所以上面的子命令使用的索引是132而不是133。

获取区域存储的值
通过使用BITFIELD命令的GET子命令,用户可以从给定的偏移量或者索引中取出指定类型的整数值:

BITFIELD bitmap GET type offset
BITFIELD bitmap GET type #index

命令中同名参数的意义完全一样。

对于下面的位图:

127.0.0.1:6379> BITFIELD bitmap SET u8 0 8 SET i32 20 88 SET i64 188 888
(integer) 0
(integer) 0
(integer) 0

在这里插入图片描述

获取位图中存储的整数值:

127.0.0.1:6379> BITFIELD bitmap GET u8 0 GET i32 20 GET i64 188
(integer) 8
(integer) 88
(integer) 888

或者根据索引存储和获取值:

127.0.0.1:6379> BITFIELD bitmap-index SET u8 #0 12 SET u8 #1 22 SET u8 #2 32
(integer) 0
(integer) 0
(integer) 0
127.0.0.1:6379> BITFIELD bitmap-index GET u8 #0 GET u8 #1 GET u8 #2
(integer) 12
(integer) 22
(integer) 32

如果用户给定的偏移量或者索引超出了位图的边界,或者给定的位图并不存在,那么GET子命令将返回0作为结果。

127.0.0.1:6379> BITFIELD bitmap-index GET u8 #888
(integer) 0

执行加法操作或减法操作
除了设置和获取整数值之外,BITFIELD命令还可以对位图存储的整数值执行加法操作或者减法操作,这两个操作都可以通过INCRBY子命令实现。

BITFIELD key [INCRBY type offset increment]

BITFIELD命令并没有提供与INCRBY子命令相对应的DECRBY子命令,但是用户可以通过向INCRBY子命令传入负数增量来达到执行减法操作的效果。INCRBY子命令在执行完相应的操作之后会返回整数的当前值作为结果。

示例:
以下代码演示了如何对一个存储着整数值10的区域执行加法操作:

127.0.0.1:6379> BITFIELD bitmap_number SET u8 #0 10
(integer) 0
127.0.0.1:6379> BITFIELD bitmap_number GET u8 #0
(integer) 10
127.0.0.1:6379> BITFIELD bitmap_number INCRBY u8 #0 25
(integer) 35
127.0.0.1:6379> BITFIELD bitmap_number GET u8 #0
(integer) 35
127.0.0.1:6379> BITFIELD bitmap_number INCRBY u8 #0 35
(integer) 70
127.0.0.1:6379> BITFIELD bitmap_number GET u8 #0
(integer) 70

区域存储的整数值执行减法操作:

127.0.0.1:6379> BITFIELD bitmap_number INCRBY u8 #0 -20
(integer) 50
127.0.0.1:6379> BITFIELD bitmap_number GET u8 #0
(integer) 50

处理溢出
BITFIELD命令除了可以使用INCRBY子命令来执行加法操作和减法操作之外,还可以使用OVERFLOW子命令去控制INCRBY子命令在发生计算溢出时的行为。

语法:

BITFIELD key [OVERFLOW WRAP|SAT|FAIL]

OVERFLOW子命令的参数可以是WRAP、SAT或者FAIL中的一个:
(1)WRAP
表示使用回绕(wrap around)方式处理溢出,这也是C语言默认的溢出处理方式。在这一模式下,向上溢出的整数值将从类型的最小值开始重新计算,而向下溢出的整数值则会从类型的最大值开始重新计算。

(2)SAT
表示使用饱和运算(saturation arithmetic)方式处理溢出,在这一模式下,向上溢出的整数值将被设置为类型的最大值,而向下溢出的整数值则会被设置为类型的最小值。

(3)FAIL
表示让INCRBY子命令在检测到计算会引发溢出时拒绝执行计算,并返回空值表示计算失败。

OVERFLOW子命令在执行时将不产生任何回复。此外,如果用户在执行BITFIELD命令时没有指定具体的溢出处理方式,那么INCRBY子命令默认使用WRAP方式处理计算溢出。

需要注意的是,因为OVERFLOW子命令只会对同一个BITFIELD调用中排在它之后的那些INCRBY子命令产生效果,所以用户必须把OVERFLOW子命令放到它想要影响的INCRBY子命令之前。比如对于以下BITFIELD调用来说,最开始的两个INCRBY子命令将使用默认的WRAP方式处理溢出,而之后的两个INCRBY子命令才会使用用户指定的SAT方式处理溢出。

127.0.0.1:6379> BITFIElD bit INCRBY ... INCRBY ... OVERFLOW SAT INCRBY 

此外,因为Redis允许在同一个BITFIELD调用中使用多个OVERFLOW子命令,所以用户可以在有需要的情况下,通过使用多个OVERFLOW子命令来灵活地设置计算的溢出处理方式。比如在以下调用中,第一个INCRBY子命令将使用SAT方式处理溢出,第二个INCRBY子命令将使用WRAP方式处理溢出,而最后的INCRBY子命令则会使用FAIL方式处理溢出:

127.0.0.1:6379> BITFIELD bitmap OVERFLOW SAT INCRBY ... OVERFLOW WRAP INCRBY ... OVERFLOW FALL INCRBY ...

示例:
在u4-nums这个位图中,存储了3个4位长无符号整数,并且它们的值都被设置成了该类型的最大值15:

127.0.0.1:6379> BITFIELD u4-nums SET u4 #0 15 SET u4 #1 15 SET u4 #2 15
(integer) 0
(integer) 0
(integer) 0
127.0.0.1:6379> BITFIELD u4-nums GET u4 #0 GET u4 #1 GET u4 #2
(integer) 15
(integer) 15
(integer) 15

如果使用INCRBY命令对这3个整数值执行加1计算,那么这3个加1计算都将发生溢出,但是通过为这些计算设置不同的溢出处理方式,这些计算最终将产生不同的结果:

BITFIELD u4-nums overflow wrap incrby u4 #0 1 overflow 
sat incrby u4 #1 1 overflow fail incrby u4 #2 1
127.0.0.1:6379> BITFIELD u4-nums overflow wrap incrby u4 #0 1 overflow sat incrby u4 #1 1 overflow fail incrby u4 #2 1
1)(integer) 0
2)(integer) 15
3)(nil)

第1个INCRBY子命令以回绕的方式处理溢出,因此整数的值在执行加1操作之后,从原来的类型最大值15回绕到了类型最小值0。因为INCRBY子命令默认就使用WRAP方式处理溢出,所以这里的第一个OVERFLOW子命令实际上是可以省略的。
第2个INCRBY子命令以饱和运算的方式处理溢出,因此整数的值在执行加1操作之后,仍然是类型的最大值15。
第3个INCRBY子命令以FAIL方式处理溢出,因此INCRBY子命令在检测到计算会引发溢出之后就放弃了执行这次计算,并返回一个空值表示计算失败。因为此次INCRBY命令未能成功执行,所以这个整数的值仍然是15。

使用位图存储整数的原因
在一般情况下,当用户使用字符串或者散列去存储整数的时候,Redis都会为被存储的整数分配一个long类型的值(通常为32位长或者64位长),并使用对象去包裹这个值,然后再把对象关联到数据库或者散列中。

与此相反,BITFIELD命令允许用户自行指定被存储整数的类型,并且不会使用对象去包裹这些整数,因此当想要存储长度比long类型短的整数,并且希望尽可能地减少对象包裹带来的内存消耗时,就可以考虑使用位图来存储整数。

使用字符串命令对位图进行操作
因为Redis的位图是在字符串的基础上实现的,所以它会把位图键看作一个字符串键。

127.0.0.1:6379> SETBIT bitmap 0 1
(integer) 0
127.0.0.1:6379> type bitmap
string

因此用户除了可以使用前面介绍的位图命令对位图进行操作之外,还可以使用字符串命令对位图进行操作。

比如,可以通过执行GET命令来获取整个位图:

127.0.0.1:6379> GET bitmap
“\x80”
127.0.0.1:6379> 

也可以使用STRLEN命令获取位图的字节长度:

127.0.0.1:6379> STRLEN bitmap
(integer) 1 (长度1为1字节)
127.0.0.1:6379> 

当使用字符串命令获取位图的值时,命令返回的是16进制字符串(\x表示十六进制),而不是一个二进制形式的位图:比如GET命令返回的就是字符串"\x80"而不是二进制位图00000100。因此在使用字符串命令操作位图的时候,必须先将命令返回的字符串转换回二进制形式,然后再执行具体的二进制操作。


Bitmaps总结

(1)Redis的位图是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
(2)BITCOUNT命令接受的是字节索引范围,而不是二进制位索引范围,忽略这一点很容易引发程序错误。
(3)BITFIELD命令允许用户自行指定被存储整数的类型,并且不会使用对象去包裹这些整数,因此当我们想要存储长度比long类型短的整数,并且希望尽可能地减少对象包裹带来的内存消耗时,就可以考虑使用位图来存储整数。
(4)位图是使用字符串实现的,所以字符串命令也可以用于处理位图命令。但是在使用字符串命令操作位图时,用户必须先把命令返回的字符串值转换成二进制值,然后再进行后续处理。


Bitmaps应用示例

为了简单实现用户在线状态功能,可以使用 Redis Bitmaps 记录用户在线状态。以下是一个简单的步骤,使用命令行工具:

(1)记录用户在线状态

使用 SETBIT 命令记录用户在线状态。这里以用户ID为键名,每一位表示一个时刻的在线状态,1表示在线,0表示离线。

# 用户1在线
127.0.0.1:6379> SETBIT user_online_status 1 1

# 用户2在线
127.0.0.1:6379> SETBIT user_online_status 2 1

# 用户3在线
127.0.0.1:6379> SETBIT user_online_status 3 1

user_online_status结果:01110000

(2)查询用户在线状态

使用 GETBIT 命令查询用户的在线状态。

# 查询用户1的在线状态
127.0.0.1:6379> GETBIT user_online_status 1
(integer) 1

(3)统计在线用户数量

使用 BITCOUNT 命令统计在线用户的数量。

# 统计在线用户数量
127.0.0.1:6379> BITCOUNT online_status
(integer) 3
  • 24
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值