Redis中的位图结构

【参考】
《Redis深度历险 核心原理与应用实践》
【位图的概念】
对于一些bool类型的数据,如是否签到的场景,虽然单个记录简单,但是需要长期、固定地去记录且用户量很大的时候,那么存储空间是特别惊人的。我们可以利用位图的结构来进行处理,这样可以节省大量空间。
位图,即在每一位上设置1或者0来标记bool类型的信息,一个字节有8位,所以对于一个需要记录一年365天的签到状态,只需要46个字节(46*8=368)就够了,如果按照2个字节一个字符的字符集格式,只需要23个字符就能够完整地记录下这个信息。
【Redis中使用位图】
我们试着把“JAVA”这个单词用位图的形式写入Redis中。“JAVA”用ASCII字符集进行转码,成为二进制后再拼接起来(注意位数组的顺序和字符的位顺序是相反的),如图:

所以将二进制拼接起来就是:01001010010000010101011001000001,该二进制数组的第一位角标为0,那么需要设置为1的位的位置为:1 4 6 9 15 17 19 21 22 25 31,我们将这些位用setbit key 位标 1的命令设置到Redis中,最终能够用get key获取到字符串。

这个可以被称为“零存整取”,当然我们也可以进行“零存零取”与“整存零取”,“零取”时使用getbit key 位标的命令。
【Redis位图的统计与查找】
上面说到的场景,如签到,到这里应该了解了如何利用位图来实现了。简单说就是每天根据实际的签到情况,setbit到今天对应的位标上。那么如果要查询这个人某一天有没有签到,我们只需要getbit key 日期对应的位标就能搞定;如果查询整个签到或者没有签到的情况,其实也可以“整取”出字符串,然后转为二进制再进行统计。
但Redis已经提供了一对方便我们进行位图查询和统计的指令,bitcount与bitpos:
bitcount key #统计整个位为1的总数
bitcount key start end #这个start与end指第几个字符开始,第几个字符结束,统计整个位为1的总数

bitpos key bit值 #返回整个key对应值二进制的第一个位值为bit值的位置
bitpos key bit值 start end #从start到end个字符中,找到第1个出现bit位值的位,在整个二进制的位置

【bitfield】
对于setbit和getbit,只能进行一位一位的操作,比如要输入一个“JAVA”,需要setbit很多次才能够达到目的。使用管道可以帮助我们解决多次交互的问题,相当于一次性输入多个指令让redis服务器来处理,再将结果返回给客户端。但在Redis3.2之后,出现了一个bitfield指令,可以指定位进行批量操作,省去了多指令的麻烦。
bitfield key get [u|i]n start #从第start个位开始,取n位来进行计数,u表示无符号位(所有的位都作为值来进行处理),i表示有符号位(第一位为符号位,后续作为值来处理)
另外,我们还可以达到跳着执行多个指令的效果:
bitfield key get u4 0 get u3 2 get O get i3 2

set子指令也可以进行批量位的替换

第三个子指令incrby,它用来对指定范围的位进行自增操作。自增就有可能出现溢出。如果增加了正数,会出现上溢出,如果增加的是负数,会出现下溢出。Redis 默认的处理是折返。如果出现了溢出,就将溢出的符号位丢掉。如果是8位无符号数255,加1后就会溢出,会全部变零。如果是8位有符号数127,加1后就会溢出变成-128。
bitfield key incrby u4 2 1 #从第三个位开始,对接下来的无符号4个位加1
bitfield指令提供了溢出策略子指令 overflow,用户可以选择溢出行为,默认是折返(wrap),还可以选择失败(fail)——报错不执行,以及饱和截断(sat)——超过了范围就停留在最大或最小值。overflow指令只影响接下来的第-条指令,这条指令执行完后溢出策略会变成默认值折返( wrap)。
关于bitfield的描述,基本上与原书一样,可以按照参考的图书章节进行学习。对于这样一个不算直观与简单的指令,没有必要精确地记住每一个参数的具体含义,这里我们只需要记得bitfield可以进行批量取位,批量替换位值,以及进行自增,而自增又会出现overflow,Redis对这种overflow进行了哪些处理,即可。在合适的场景中,我们如果能想到有这么个功能,可以为我们提供便捷,即可,具体的参数可以在使用时仔细留意。

原文地址:https://www.cnblogs.com/bruceChan0018/p/15652359.html