签到功能怎么做?Bitmaps 助你一臂之力
1、简介
Bitmaps 称为位图,它不是一种数据类型。网上很多视频教程把 Bitmaps 称为数据类型,应该是不正确的。Bitmaps 是 Redis 提供给使用者用于操作位的“数据类型”。它主要有如下的基本特性:
Bitmaps 不是数据类型,底层就是字符串(key-value),byte 数组。我们可以使用普通的 get/set 直接获取和设值位图的内容,也可以通过 Redis 提供的位图操作 getbit/setbit 等将 byte 数组看成“位数组”来处理
Bitmaps 的“位数组”每个单元格只能存储 0 和 1,数组的下标在 Bitmaps 中称为偏移量
Bitmaps 设置时 key 不存在会自动生成一个新的字符串,如果设置的偏移量超出了现有内容的范围,就会自动将位数组进行零扩充
2 、基本操作
2.1 SETBIT key offset value
对 key 存储的字符串,设置或者清除指定偏移量上的位(bit),位的设置或者清除取决于 value 参数,0/1;当 key 不存在时,自动生成一个新的字符串。字符串会进行伸展确保 value 保存在指定的偏移量上。字符串进行伸展时,空白位置以 0 填充。
时间复杂度 :
O(1)
offset 范围:
0~2^32
返回值:
指定偏移量原来存储的位
案例:
使用 Bitmaps 来存储用户是否打卡,打卡记做 1,未打卡为 0,用户的 id 作为偏移量
假设存在 10 个用户,此时用户 1、3、5、9、10 打了卡,其他人未打卡,Bitmaps 的初始化结果如下所示:
clock:20210806 代表 2021/08/06 的打卡记录
注意事项:
正式系统中,id 肯定不会是 0、1、2 这种,而是以某一个数组开头,比如 1000000000000001、1000000000000002 这个时候非常容易导致偏移量的浪费,因此我们可以考虑通过计算减去一个合适的值后再设置偏移量,如果设置的 Bitmaps 偏移量过大,容易造成分配内存时间过长,Redis 服务器被阻塞。
2.2 GETBIT key offset
获取指定偏移量上的位(bit),当 offset 比字符串长度大,或者 key 不存在,返回 0;
时间复杂度:
O(1)
返回值:
字符串值指定偏移量上的位(bit)
案例:
clock:20210806 代表 2021/08/06 的打卡记录
2.3 BITCOUNT key [start] [end]
计算给定字符串中,被设置为 1 的 bit 位的数量。start 和 end 参数可以指定查询的范围,可以使用负数值。-1 代表最后一个字节,-2 代表倒是第二个字节。
注意:start 和 end 是字节索引,因此每增加 1 代表的是增加一个字符,也就是 8 位,所以位的查询范围必须是 8 的倍数。
时间复杂度:
O(N)
返回值:
被设置为 1 的位的数量
案例:
clock:20210806 代表 2021/08/06 的打卡记录,此时一共 11 位,前 8 位置 3 个 1,后 3 位中 2 个 1
bitcount clock:20210806 0 0 表示第 1 个字符中 1 的个数
bitcount clock:20210806 1 1 表示第 2 个字符中 1 的个数
bitcount clock:20210806 0 1 表示第 1 和第 2 个字符中 1 的个数
2.4 BITPOS key bit [start] [end]
返回第一个置为 bit 的二进制位的位置,默认检测整个 Bitmaps,也可以通过 start 和 end 参数指定查询范围
注意:start 和 end 是字节索引,因此每增加 1 代表的是增加一个字符,也就是 8 位,所以位的查询范围必须是 8 的倍数。
时间复杂度:
O(N)
返回值:
整数回复
案例:
bitpos clock:20210806 0 表示第一个 0 的位置
bitpos clock:20210806 1 表示第一个 1 的位置
bitpos clock:20210806 1 0 0 表示第一个字符中,第一个 1 的位置
bitpos clock:20210806 1 1 1 表示第二个字符中,第一个 1 的位置
bitpos clock:20210806 1 0 1 表示第一个和第二个字符中,第一个 1 的位置
2.5 BITOP operation destkey key [key …]
Redis 的 Bitmaps 提供 BITOP 指令来对一个或多个(除了 NOT 操作)二进制位的字符串 key 进行位元操作,操作的结果保存到 destkey 上,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
当字符串长度不一致是,较短的那个字符串所缺失的部分会被看作 0,空的 key 也会被看作是包含 0 的字符串序列
时间复杂度:
O(N)
返回值:
位运算的结果(保存到 destkey 的字符串的长度和输入 key 中的最长的字符串的长度相等)
案例:
这里使用 key1 1001 和 key2 1011 进行上述四种操作
BITOP AND destkey key [key ...]
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为 0
BITOP OR destkey key [key ...]
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为 1,其值为 1
BITOP XOR destkey key [key ...]
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为 1,否则为 0
BITOP NOT destkey key
运算规则:取反
2.6 BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
2.1 和 2.2 中的 setbit 和 getbit 都是对指定 key 的单个位的操作,如果需要对多个位同时操作,那么可以使用 bitfield 指令,bitfield 有三个子指令,分别是 get、set、incrby,它们可以对指定的片段进行读写,但是最多处理 64 个连续的位,超过 64 个连续的位,需要使用多个子指令,bitfield 可以同时执行多个子指令(无符号整数只能返回 63 位)。
注意:
使用 GET 子命令对超出字符串当前范围的二进制位进行访问(包括键不存在的情况), 超出部分的二进制位的值将被当做是 0 。
使用 SET 子命令或者 INCRBY 子命令对超出字符串当前范围的二进制位进行访问将导致字符串被扩大, 被扩大的部分会使用值为 0 的二进制位进行填充。 在对字符串进行扩展时, 命令会根据字符串目前已有的最远端二进制位, 计算出执行操作所需的最小长度。
值操作子指令:
GET <type> <offset> —— 返回指定的二进制位范围
SET <type> <offset> <value> —— 对指定的二进制位范围进行设置,并返回它的旧值
INCRBY <type> <offset> <increment> —— 对指定的二进制位范围执行加法操作,并返回它的旧值。用户可以通过向 increment 参数传入负值来实现相应的减法操作
溢出策略子指令:
WRAP:回绕/折返(wrap around)-默认溢出策略,对于无符号整数来说, 回绕就像使用数值本身与能够被储存的最大无符号整数执行取模计算, 这也是 C 语言的标准行为。 对于有符号整数来说, 上溢将导致数字重新从最小的负数开始计算, 而下溢将导致数字重新从最大的正数开始计算。
SAT:饱和计算(saturation arithmetic),也可以理解为饱和截断,这种模式下下溢计算的结果为最小的整数值, 而上溢计算的结果为最大的整数值
FAIL:失败不执行,这种模式会拒绝执行那些导致上溢或者下溢的计算情况,返回 nil 表示计算未被执行。
需要注意的是, OVERFLOW 子命令只会对紧随着它之后被执行的 INCRBY 命令产生效果, 这一效果将一直持续到与它一同被执行的下一个 OVERFLOW 命令为止。 在默认情况下, INCRBY 命令使用 WRAP 方式来处理溢出计算。
i 与 u:
i 表示有符号整数,u 表示无符号整数。u4 代表 4 位长的无符号整数,i8 代表 8 位长的有符号整数。
案例:
测试数字为 10100111
bitfield key get u4 0 从第一个位开始取 4 个位,得到无符号数 1010=10
bitfield key set u8 0 128 从第 0 个开始,将接下来的 8 位用无符号整数 128 替换,也就是 10000000
bitfield key incrby u4 2 1 从第 2 位开始对接下来的 4 位无符号数+1
bitfield key set u8 0 128 get u4 0 incrby u4 2 1 复合指令,是上面三者的组成,返回值是每个操作的子集,相当于管道操作
版权声明: 本文为 InfoQ 作者【李子捌】的原创文章。
原文链接:【http://xie.infoq.cn/article/e4488618bc6935c02d123af9a】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论