Redis学习之旅 位图(BitMap)

Redis学习之旅 位图(BitMap)

单独给位图开一章,是因为这块的东西确实玩法众多,面试也常常遇到,干脆将其拆分为独立的一章

Redis BIT相关命令

BITCOUNT

从字面意思上来看,bitcount翻译过来叫位统计,那么统计的是什么呢?,其实是01串中1的个数

  • OK,这不简单么,我知道了,字符串1 BITCOUNT的结果肯定是1,因为1的二进制是 0000 0001嘛,我们看下结果
    在这里插入图片描述

我了个大擦,怎么会是3?,这就又回到我们在String篇里提到的一个概念,Redis String类型的数据,都是按一个字节一个字节来保存的,Redis实际上处理的是一个个的字节,每个字节又如何去表示呢?不管编码字符集是什么,它有一个是不变的,就是ASCII码,其他已知的字符集(UTF-8,GBK)都是在ASCII码的基础上进行的扩展

ASCII 码表


通过对照表可以看到,字符1在ASCII里的表示 ,实际上赞同于十进制的49,转成二进制是 00110001,数一下,确实是3个1

  • NICE!那我就明白了,后面的start 和 end 明显就是二进制位的偏移么,如果我把start定为3 ,end 定为7,一定会返回2,因为从第四位到第八位,里面有两个1

Redis学习之旅 位图(BitMap)_第1张图片
在这里插入图片描述
-惊不惊喜?意不意外?是的,虽然bitcount是位级别的统计,但是它的参数并不是位级别的,而是字节级别的!

Redis学习之旅 位图(BitMap)_第2张图片

从上面可以得出几个结论

  1. bitcount是可以越界的,直接返回0
  2. 针对不存在的key,直接返回0
  3. 它的start,end参数在使用与含义上等于SETRANGE、GETRANGE,是字节级别的偏移

GETBIT

对 key 所储存的字符串值,获取指定偏移量上的位,怎么理解呢?看下面的例子。还是以1为例 ,我们已经知道,它对应的ascii位置为49,对应的二进制是00110001
从执行的结果可以看出

  1. 位获取是从高位向低位进行计算的
  2. 获取到的是指定偏移后的位上的0或者1
  3. 越界直接返回0

Redis学习之旅 位图(BitMap)_第3张图片
有人可能会想,那么多个字符咋办?演示一下

推算一下,13= 8+5,即求的是第二个字符的第6位,我们看下b的二进制 01100010,第6位为0,第7位为1

Redis学习之旅 位图(BitMap)_第4张图片
接着提问题,中文呢?提到中文就不得不说下编码,UTF-8对中文的编码占三个字节,GBK编码占两个字节,以UTF-8为例进行测试
在这里插入图片描述
可以看到,确实是一个汉字占据三个字节,每个字节内填充的是一个16进制数,直接取第3个字节,即\xad,由16进制转换为2进制为10101101,看下结果完全匹配
Redis学习之旅 位图(BitMap)_第5张图片

SETBIT

这块和GETBIT完全一致,就是把对应的位上修改为0或者1,简单演示一下
1.设置的位的有效值只有0/1,其他值会抛出异常

Redis学习之旅 位图(BitMap)_第6张图片

BITPOS

即Bit Position,查找指定位的准确位置,啥意思呢?就是在给定的字符串中,按二进制,找到第一个你需要查找的0或者1,并返回它的位索引位置,\xe4 对应二进制 11100100

  1. 不给出偏移时,是从第一个字符开始计算,找到一个匹配的就返回
  2. 无法按位给出偏移量,只能按字节来给范围

Redis学习之旅 位图(BitMap)_第7张图片

BITOP

字面意思字节操作,实际指可以将多个字符串进行位运算,并将结果存放到另一个指定Key上
支持如下四种操作

  1. BITOP AND destkey key [key …] ,对一个或多个key求位与,并将结果保存到destkey
  2. BITOP OR destkey key [key …] ,对一个或多个key求位或,并将结果保存到destkey
  3. BITOP XOR destkey key [key …] ,对一个或多个key求位异或,并将结果保存到destkey
  4. BITOP NOT destkey key ,对给定key求位非,并将结果保存到destkey

BITOP AND

  • 按位与,a 对应的ascii二进制 01100001 ,b对应的ascii二进制为01100010
    01100001 & 01100010 = 01100000,即字符 `

Redis学习之旅 位图(BitMap)_第8张图片

BITOP OR

  • 01100001 | 01100010 = 01100011,即字符 c
    Redis学习之旅 位图(BitMap)_第9张图片

BITOP XOR

  • 01100001 ^ 01100010 = 00000011,即字符 ●

Redis学习之旅 位图(BitMap)_第10张图片

BITOP NOT

需要注意的是,位非操作只可以有一个key参与运算

  • ~01100001 = 10011110

Redis学习之旅 位图(BitMap)_第11张图片

BITFIELD

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
按Redis提示,它是在string中取某些位做int类型的运算
可以一次对多个位进行位操作.三个子指令get,set,incrby都可以读写指定位的片段,但一次最多只能处理64个连续的位,如果超过64位,则需要使用多条命令实现,只能取64位是因为Redis对int类型的数据定义就是64位,如果算上符号位,可用位只有63位

Redis学习之旅 位图(BitMap)_第12张图片
先算一下对应的位,防止后面懵圈

字符 ASCII码序号(十进制) 对应的二进制
h 104 01101000
e 101 01100101
l 108 01101100
o 111 01101111
, 44 00101100
w 119 01110111
r 114 01110010
d 100 01100100
! 33 00100001

BITFIELD GET

bitfield k1 get u4 17 :偏移17位,取出4位二进制码转换成无符号十进制
在K1的这个Value的字符串"hello,world!"中,获取第18位起,连续的3个字节,即字符“L”的第2位开始,向后取连续的4个位,即 0<开始>1101<结束>100,然后按无符号计算,1101转十进制,等于13

Redis学习之旅 位图(BitMap)_第13张图片

bitfield k1 get i4 17:偏移17位,取出4位二进制码转成带符号十进制
同样取出1101,这是一个负数
原码1101,反码 0010,补码0011,0011即为3,计算结果应该为-3

Redis学习之旅 位图(BitMap)_第14张图片

BITFIELD SET

set和get的思路就是反着来,把给定的位使用你给定的十进制数进行填充
BITFIELD k1 set u4 2 13 ,就是将13转为无符号二进制1101,从第3个位开始,连续替换4位,原来的h是01101000,替换完后成了01110100,二进制对应116,即t

Redis学习之旅 位图(BitMap)_第15张图片

  1. 如果转出的数字(1101)小于要覆盖的范围(14),高位补0

Redis学习之旅 位图(BitMap)_第16张图片

  1. 如果转出的数字(100110)大于要覆盖的范围(4),计算的结果为00011000,明显,这是把高位,连带符号位一起扔掉了
    Redis学习之旅 位图(BitMap)_第17张图片

BITFIELD INCRBY

前面都是对位的直接覆盖处理,这里就开始玩计算了,计算就会有溢出,Redis提供了三种策略

  • WRAP: 回环算法(默认),适用于有符号和无符号整型两种类型。对于无符号整型,回环计数将对整型最大值进行取模操作(C语言的标准行为)。对于有符号整型,上溢从最负的负数开始取数,下溢则从最大的正数开始取数,例如,如果i8整型的值设为127,自加1后的值变为-128。
  • SAT: 饱和算法,下溢之后设为最小的整型值,上溢之后设为最大的整数值。例如,i8整型的值从120开始加10后,结果是127,继续增加,结果还是保持为127。下溢也是同理,但量结果值将会保持在最负的负数值。
  • FAIL: 失败算法,这种模式下,在检测到上溢或下溢时,不做任何操作。相应的返回值会设为NULL,并返回给调用者。

一定要注意要把overflow配置到incrby前面 ,否则不报错,不生效,使用默认wrap算法

Redis学习之旅 位图(BitMap)_第18张图片

位图运用

登录统计

一、统计某个时间段内的日活情况

解决思路,将用户编号,每个用户固定占用某个编号,不随用户的变更进行编号调整
按天开辟可以容纳用户的位行,举例,现在有100个用户,16个字节 16*8=128,就可以开辟出128个位0,将用户从0编号到99,以天创建key,用户登录后,与用户编号对应位置进行与1操作

  • 准备三天的位

Redis学习之旅 位图(BitMap)_第19张图片

  • 假定有四个用户,ID号分别是 12、35、54、83
日期 12 35 54 83
20200526 在线 不在线 不在线 在线
20200527 不在线 在线 在线 在线
20200528 在线 在线 在线 不在线
  • 将每天在线的用户标志位打上1

Redis学习之旅 位图(BitMap)_第20张图片

  • 查看27号的日活

在这里插入图片描述

  • 查看这三天一共有多少人没登录
    100-4=96人没有登录

在这里插入图片描述

你可能感兴趣的:(#,Redis)