概念
-
Redis
提供的Bitmaps这个“数据结构”可以实现对位的操作。Bitmaps本身不是一种数据结构,实际上就是字符串,但是它可以对字符串的位进行操作。 -
可以把Bitmaps想象成一个以位为单位的数组,数组中的每个单元只能存0或者1,数组的下标在bitmaps中叫做偏移量。
-
单个bitmaps的最大长度是
512MB
,即2^32个比特位。
例如字符串A 一个字节,对应的ASCII码是65,对应的二进制就是01000001,Bitmaps就是对A的二进制位进行操作。
命令
SETBIT
SETBIT key offset value
summary: Sets or clears the bit at offset in the string value stored at key
设置键的第offset个位的值,value只有0和1两个值
字符串A在位数组的 1和7两个位置是1(从0算起)
127.0.0.1:6379[1]> setbit k1 1 1
(integer) 0
127.0.0.1:6379[1]> setbit k1 7 1
(integer) 0
127.0.0.1:6379[1]> get k1
"A"
127.0.0.1:6379[1]> strlen k1
(integer) 1
对 位1和7设置成1,可以得到字符串A,这时k1只有8位,1个字节,所以长度为1,接下来再对k1进行如下设置
127.0.0.1:6379[1]> setbit k1 9 1
(integer) 0
127.0.0.1:6379[1]> setbit k1 14 1
(integer) 0
127.0.0.1:6379[1]> get k1
"AB"
127.0.0.1:6379[1]> strlen k1
(integer) 2
这时k1
的值是如下图,占2个字节
GETBIT
GETBIT key offset
summary: Returns the bit value at offset in the string value stored at key
获取键的第offset位的值
127.0.0.1:6379[1]> getbit k1 1
(integer) 1
127.0.0.1:6379[1]> getbit k1 2
(integer) 0
BITCOUNT
BITCOUNT key [start end]
summary: Count set bits in a string
获取指定范围值为1的个数,其中start和end代表起始和结束字节数
127.0.0.1:6379[1]> bitcount k1 0 0
(integer) 2
127.0.0.1:6379[1]> bitcount k1 1 1
(integer) 2
127.0.0.1:6379[1]> bitcount k1 0 1
(integer) 4
BITPOS
BITPOS key bit [start] [end]
summary: Find first bit set or clear in a string
计算Bitmaps中的第一值为0或者1的偏移量,start和end分别代表起始字节和结束字节
127.0.0.1:6379[1]> bitpos k1 1 0 0
(integer) 1
127.0.0.1:6379[1]> bitpos k1 1 1 1
(integer) 9
127.0.0.1:6379[1]> bitpos k1 0 1 1
(integer) 8
对比上图k1
值的位数组进行验证
BITOP
BITOP operation destkey key [key ...]
summary: Perform bitwise operations between strings
bitop
是一个复合操作,可以做多个Bitmaps的and
、or
、not
、xor
操作。
operation
可以是 AND
、 OR
、 NOT
、 XOR
这四种操作中的任意一种:
BITOP AND destkey key [key ...]
,对一个或多个key
求逻辑并,并将结果保存到destkey
。BITOP OR destkey key [key ...]
,对一个或多个key
求逻辑或,并将结果保存到destkey
。BITOP XOR destkey key [key ...]
,对一个或多个key
求逻辑异或,并将结果保存到destkey
。BITOP NOT destkey key
,对给定key
求逻辑非,并将结果保存到destkey
设置k2
01000001
127.0.0.1:6379[1]> setbit k2 1 1
(integer) 0
127.0.0.1:6379[1]> setbit k2 7 1
(integer) 0
127.0.0.1:6379[1]> get k2
"A"
设置k3
01000010
127.0.0.1:6379[1]> setbit k3 1 1
(integer) 0
127.0.0.1:6379[1]> setbit k3 6 1
(integer) 0
127.0.0.1:6379[1]> get k3
"B"
AND
127.0.0.1:6379[1]> bitop and anddest k2 k3
(integer) 1
127.0.0.1:6379[1]> get anddest
"@"
k2
01000001 和 k3
01000010做与运算(有0为0,全1为1) 结果 01000000 对应的ASCII码是@
OR
127.0.0.1:6379[1]> bitop or ordest k2 k3
(integer) 1
127.0.0.1:6379[1]> get ordest
"C"
k2
01000001 和 k3
01000010做或运算(有1为1全0为0) 结果 01000011 对应的ASCII码是C
XOR
127.0.0.1:6379[1]> bitop xor xordest k2 k3
(integer) 1
127.0.0.1:6379[1]> get xordest
"\x03"
k2
01000001 和 k3
01000010做异或运算(相同为0,不同为1) 结果 00000011 对应的十六进制\x03 十进制下3
NOT
127.0.0.1:6379[1]> bitop not notdest k2
(integer) 1
127.0.0.1:6379[1]> get notdest
"\xbe"
对k2
01000001 做非运算(非0则1,非1则0) 结果是 10111110 对应的十六进制\xbe
BITFIELD
BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
summary: Perform arbitrary bitfield integer operations on strings
redis3.2
后新增了一个bitfield
命令,可以一次对多个位进行操作.这个指令有三个子指令,get,set,incrby
,都可以对指定位片段进行读写,但最大支持 64 位长的有符号整数以及 63 位长的无符号整数, 其中无符号整数的 63 位长度限制是由于 Redis
协议目前还无法返回 64 位长的无符号整数而导致的。。
BITFIELD
命令支持的子命令:
GET <type> <offset>
—— 返回指定的二进制位范围。SET <type> <offset> <value>
—— 对指定的二进制位范围进行设置,并返回它的旧值。INCRBY <type> <offset> <increment>
—— 对指定的二进制位范围执行加法操作,并返回它的旧值。用户可以通过向increment
参数传入负值来实现相应的减法操作。
除了以上三个子命令之外, 还有一个子命令, 它可以改变之后执行的 INCRBY
子命令在发生溢出情况时的行为:
OVERFLOW [WRAP|SAT|FAIL]
当被设置的二进制位范围值为整数时, 用户可以在类型参数的前面添加 i
来表示有符号整数, 或者使用 u
来表示无符号整数。 比如说, 我们可以使用 u8
来表示 8 位长的无符号整数, 也可以使用 i16
来表示 16 位长的有符号整数。
127.0.0.1:6379[1]> set k4 A
OK
#A对应的二进制 01000001
#从第1个位开始读取4个位 0100 结果为无符号数(u)
127.0.0.1:6379[1]> bitfield k4 get u4 0
1) (integer) 4
#从第2个位开始读取2个位 10 结果为无符号数(u)
127.0.0.1:6379[1]> bitfield k4 get u2 1
1) (integer) 2
#从第0个位开始读取3个位 010 结果为有符号数(i)
127.0.0.1:6379[1]> bitfield k4 get i3 0
1) (integer) 2
#从第1个位开始读取5个位 10000 结果为有符号数(i) 首位是1代表负数,对10000减1 -> 01111 取反 -> 10000 十进制下是16,由于符号位是1,所以结果是-16
127.0.0.1:6379[1]> bitfield k4 get i5 1
1) (integer) -16
二进制位和位置偏移量
在二进制位范围命令中, 用户有两种方法来设置偏移量:
- 如果用户给定的是一个没有任何前缀的数字, 那么这个数字指示的就是字符串以零为开始(zero-base)的偏移量。
- 另一方面, 如果用户给定的是一个带有
#
前缀的偏移量, 那么命令将使用这个偏移量与被设置的数字类型的位长度相乘, 从而计算出真正的偏移量。
127.0.0.1:6379[1]> BITFIELD k5 SET i8 #0 100
1) (integer) 0
127.0.0.1:6379[1]> BITFIELD k5 SET i8 #1 200
1) (integer) 0
# 01100100 11001000
命令会把 k5
键里面, 第一个 i8
长度的二进制位的值设置为 100
, 并把第二个 i8
长度的二进制位的值设置为 200
。 当我们把一个字符串键当成数组来使用, 并且数组中储存的都是同等长度的整数时, 使用 #
前缀可以让我们免去手动计算被设置二进制位所在位置的麻烦。
应用
统计用户登录天数
构建长度为天数365的位数组,哪天登录过,设置value为1,这样每个用户最多占用46个字节
127.0.0.1:6379[1]> setbit jack 1 1 #第2天登录过
(integer) 0
127.0.0.1:6379[1]> setbit jack 8 1 #第9天登录过
(integer) 0
127.0.0.1:6379[1]> setbit jack 364 1 #第365天登录过
(integer) 0
127.0.0.1:6379[1]> strlen jack
(integer) 46
127.0.0.1:6379[1]> bitcount jack #登录的天数
(integer) 3
统计每天的活跃用户数量
位数组,每一位代表用户的ID
127.0.0.1:6379[1]> setbit 2020-11-11 1 1 #用户ID为1用户活跃
(integer) 0
127.0.0.1:6379[1]> setbit 2020-11-11 30 1 #用户ID为30用户活跃
(integer) 0
127.0.0.1:6379[1]> setbit 2020-11-11 184 1 #用户ID为184用户活跃
(integer) 0
127.0.0.1:6379[1]> bitcount 2020-11-11 #统计2020-11-11的活跃量
(integer) 3
像签到、点赞、评论数都可以用Bitmaps来实现