1.1.1. 04.位图


Redis 可以做位运算。如同正常的位运算一样。

用处:

  • 用户签到:月签,年签,利用 Redis 的位图可以轻松统计出用户每月/年的签到次数。想想如果有上亿用户,签到需要的空间是非常惊人的。
  • 用户在线人数统计:用户 id 主键为比特位,在线为 1,不在线为 0,通过位图轻松记录有多少人在线,或者确定谁在线。1亿用户只需要 10M 的大小就能做到(100000000/1024(mb)/1024(kb)/8(bit))。

基本用法

如 'h' 这个字符,通过 Python 的 bin(ord('h')) 可以将 h 转换为 ASCII 码「104」,再转换为二进制即 「01101000」。

比特位 0 1 2 3 4 5 6 7
比特值 0 1 1 0 1 0 0 0

通过 set 方法创建一个 h,然后通过 getbit 来获取转化成二进制的字符在指定比特位的值。

获取指定位置的比特位的值 getbit [key] [offset]

127.0.0.1:6379> set demo h # 设置一个键为 h,其二进制是 01101000
OK
127.0.0.1:6379> getbit demo 0 # 比特位第一位 0
(integer) 0
127.0.0.1:6379> getbit demo 1 # 比特位第一位 1
(integer) 1
127.0.0.1:6379> getbit demo 2
(integer) 1
127.0.0.1:6379> getbit demo 3
(integer) 0
127.0.0.1:6379> getbit demo 4
(integer) 1
127.0.0.1:6379> getbit demo 5
(integer) 0
127.0.0.1:6379> getbit demo 6
(integer) 0
127.0.0.1:6379> getbit demo 7
(integer) 0

第二个例子,我们通过设置比特位「01101000」创建一个 h 字符。

设置指定位置的比特位的值 setbit [key] [offset] [value]

127.0.0.1:6379> setbit demo2 0 0 
(integer) 0
127.0.0.1:6379> setbit demo2 1 1
(integer) 0
127.0.0.1:6379> setbit demo2 2 1
(integer) 0
127.0.0.1:6379> setbit demo2 3 0
(integer) 0
127.0.0.1:6379> setbit demo2 4 1
(integer) 0
127.0.0.1:6379> setbit demo2 5 0
(integer) 0
127.0.0.1:6379> setbit demo2 6 0
(integer) 0
127.0.0.1:6379> setbit demo2 7 0
(integer) 0
127.0.0.1:6379> get demo2
"h"

当对应的字节不可打印时,则 Redis 会返回一个十六进制的值

127.0.0.1:6379> setbit x 0 1
(integer) 0
127.0.0.1:6379> setbit x 1 1
(integer) 0
127.0.0.1:6379> get x
"\xc0"

统计与查找

统计一个键从第 start 个「字符」到第 end 个「字符」的「比特位」有多少个「1」,bitcount [key] [start] [end]

注意这里的 start 是字符,如 hello 的第一个字符是 h。

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitcount w # hello 转换成二进制后有 211
(integer) 21
127.0.0.1:6379> bitcount w 0 0 # 统计第一个字符 h 中有多少个 1(integer) 3
127.0.0.1:6379> bitcount w 0 4 # 统计从第一个字符开始到第五个字符有多少个 1
(integer) 21
127.0.0.1:6379> bitcount w 1 4 # 统计从第二个字符开始到第五个有多少个 1
(integer) 18

返回从第 start 个字符到第 end 个字符之间,第一个比特值为 1 的比特位的位置,bitpos [key] [start] [end]

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitpos w 1 1 1 # 从第二个字节开始,第一次出现的 1 的比特位是 9(integer) 9
127.0.0.1:6379> bitpos w 1 2 2 # 从第三个字节开始,第一次出现的 1 的比特位是 17(integer) 17

运算

对一个或多个 key 进行位运算。

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。注意这里只能是一个 key

示例:

127.0.0.1:6379> set a a
OK
127.0.0.1:6379> set c c
OK
127.0.0.1:6379> bitop and result a c
(integer) 1
127.0.0.1:6379> get result
"a"
127.0.0.1:6379> bitop or result2 a c
(integer) 1
127.0.0.1:6379> get result2
"c"
127.0.0.1:6379> bitop xor result3 a c
(integer) 1
127.0.0.1:6379> get result3
"\x02"
127.0.0.1:6379> bitop not result4 a c # 注意这里取否只能是一个 key,两个会报错
(error) ERR BITOP NOT must be called with a single source key.
127.0.0.1:6379> bitop not result4 a
(integer) 1
127.0.0.1:6379> get result4
"\x9e"

魔术指令 bitfield

我们设置 (setbit)和获取(getbit)指定位的值都是单个位的, 如果要一次 操作多个位,就必须使用管道来处理。 在 Redis3.2 版本之后,可以使用 bitfield 来操作多个位。 其中 bitfield 有三个子指令 (get set incrby)。它们都可以对指定位片段进行 读写,但是最多只能处理 64 个连续的位,如果超过 64 位,就得使用多个子指令。

  1. 返回指定的二进制范围, get [u|i][offset] [start]

u 为无符号数,即没有符号位,获取到的位数组全部都是值。 i 为有符号位,即第一位是符号位,剩下的都是值。 有符号数最多可以获取 64 位,无符号数只能获取 63 位(因为 Redis 协议中的 integer 是有符号数,最大 64 位,不能传递 64 位无符号值〉。如果超出限制,则 Redis 会报错。

127.0.0.1:6379> setbit w 0 1
(integer) 0
127.0.0.1:6379> setbit w 1 1
(integer) 0
127.0.0.1:6379> setbit w 2 1
(integer) 0
127.0.0.1:6379> setbit w 3 1
(integer) 0
127.0.0.1:6379> get w # 把 w 设置成 1111
"\xf0"

127.0.0.1:6379> bitfield w get u4 0 #0 开始取 4 位无符号数。
(integer) 15
127.0.0.1:6379> bitfield w get i4 0 #0 开始取 4 位有符号数。
(integer) -1

127.0.0.1:6379> bitfield w get u3 2 # 从第 3 位开始取 3 个无符号数。二进制 110 ,十进制 6
(integer) 6

127.0.0.1:6379> bitfield w get i2 3 # 从第 4 位开始取 2 个有符号数。二进制为 10,有符号转十进制要进行补码(即反码后+1),则 10 反码为 01+1,为 10,符号位负,所以十进制为 -2(integer) -2
  1. 对指定的二进制范围进行设置,并返回它的旧值 set [u|i][offset] [start]

    127.0.0.1:6379> set w hello
    OK
    129.0.0.1:6379> bitfield w set u8 8 97 #从第 9 个位开始,将接下来的 8 个位用无符号数 97 替换
    (integer) 101 
    127.0.0.1:6379> get w 
    "hallo"
    
  2. 对指定的二进制范围执行加法操作,并返回它的旧值。可以通过 increment 参数传入负值来进行减法操作。 [overflow] [warp|fail|sat] incrby [u|i][offset] [increment] Redis 提供了溢出策略的子命令:

  3. warp (默认):使用回绕的方法来处理有符号整数和无符号整数的溢出情况
    • 无符号:回绕就像使用数值本身与能够被储存的最大无符号整数执行取模计算,这也是C语言的标准行为。
    • 有符号:上溢将导致数字重新从最小的负数开始计算,而下溢将导致数字重新从最大的正数开始计算。比如:127 的i8执行加一操作 那么得到的结果是-128
  4. sat:使用饱和计算方法处理溢出,也就是说,下溢计算的结果为最小的整数值,而上溢计算的结果为最大的整数值
    • 例子:如果对一个值为120的i8整数执行加10计算,那么命令的结果将i8所能存储的最大整数值为127。相反,如果针对i8值计算造成了下溢,那么这个i8值将被设置为-127。
  5. fail:在这一模式下,命令将拒绝执行那些会导致上溢或者下溢情况出现的计算,并向用户返回空值表示计算未被执行。

warp 例子:

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 11
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 12
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 13 
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 14 
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 15 
127.0.0.1:6379> bitfield w incrby u4 2 1
(integer) 0


127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitfield w incrby i4 2 1
(integer) -5
127.0.0.1:6379> bitfield w incrby i4 2 1
(integer) -4
127.0.0.1:6379> bitfield w incrby i4 2 1
(integer) -3
127.0.0.1:6379> bitfield w incrby i4 2 1
(integer) -2
127.0.0.1:6379> bitfield w incrby i4 2 1
(integer) -1

sat 例子:

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 11
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 12
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 13 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 14 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 15 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1
(integer) 15

fail 例子:

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1
(integer) 11
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1
(integer) 12
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1
(integer) 13 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1
(integer) 14 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1
(integer) 15 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 #不执行
(nil)
Copyright © Kagami丶 2019 all right reserved,powered by Gitbook该文件修订时间: 2019-12-10 21:45:40

results matching ""

    No results matching ""