1. 场景:
记录用户一年的签到记录,如果用String存储,需要使用365个key/value,操作起来麻烦 ,通过位图可以有效的简化这个操作。 它的统计很简单:如签到记为1,没有签到记为0,每天的记录占一个位:
1011011111 这样大概需要46个字节(365天占365位,一个字节8位),可以有效的节省存储空间。如果有一天想要统计用户一共签到的天数,只需要统计1的位数即可。
对于位图的操作,可以直接操作对应的字符串的(get/set),可以直接操作位(getbit/setbit).
2. 基本操作
Redis的基本操作可以归为两大类:
2.1. 零存整取
例如存储一个java字符串:(存储时是一个一个的,取出的时候按字符串一次取出)
接下来去存储:
2.2. 整存零取
存一个字符串进去,但通过位操作获取字符串。
3.统计
例如签到记录:
0111001111
1表示1签到的天,0表示没签到,统计总签到的天数,可以使用 bitcount :
127.0.0.1:6379> BITCOUNT name (integer) 14
bitcount中,可以统计的起始位置,但是注意,这个起始位置是指字符串的起始位置,而不是bit的起始位置。
127.0.0.1:6379> BITCOUNT name 0 0 (integer) 3 127.0.0.1:6379> BITCOUNT name 0 1 (integer) 6 127.0.0.1:6379>
除了bitcount之外,还有一个bitpos。 bitpos可以用来统计在指定范围内出现的第一个 1 或者 0 的位置,而这个命令的起始位置和结束位置都是字符索引,不是bit索引,需要注意。
name为Java
# 获取第一个 1的位置, 可见Java中 位数据 01001010 01100001 01110110 01100001 127.0.0.1:6379> BITPOS name 1 (integer) 1 # 获取第一个 0 的位置 127.0.0.1:6379> BITPOS name 0 (integer) 0 # 获取第一个 0 的位置 (从第一个字符开始,到第一个字符结束), 对应于a的第一个0的位置 127.0.0.1:6379> BITPOS name 0 1 1 (integer) 8
4.Bit批处理
在redis3.2之后,新加了一个功能叫做bitfield, 可以对bit进行批量操作。
例如:
BITFIELD name get u4 0
表示获取 name 中的位,从 0 开始获取,获取 4 位,返回一个无符号的数字。
u 表示无符号的数字
i 表示有符号的数字,有符号的话,第一个符号就表示符号位,1 表示一个负数。
127.0.0.1:6379> BITFIELD name get u4 0 1) (integer) 4 127.0.0.1:6379> BITFIELD name get i4 1 1) (integer) -7 127.0.0.1:6379> BITFIELD name get u4 1 1) (integer) 9 127.0.0.1:6379>
bitfield 也可以一次执行多个操作。
GET:
127.0.0.1:6379> BITFIELD name get u4 1 get i4 1 get u4 0 get i4 0 1) (integer) 9 2) (integer) -7 3) (integer) 4 4) (integer) 4 127.0.0.1:6379>
SET:
用无符号的 98 转换成的8位二进制数字,代替从第 8 位开始接下来的 8 位数字。
127.0.0.1:6379> get name "Java" 127.0.0.1:6379> BITFIELD name set u8 8 98 1) (integer) 97 127.0.0.1:6379> get name "Jbva" 127.0.0.1:6379>
INCRBY:
对指定范围进行自增操作,自增操作可能会出现溢出,既可能是向上溢出,也可能是向下溢出。 Redis中对于溢出的处理方案是折返。 8位无符号位数 255 加 1 溢出变为0; 8位有符号数127,加1 变为 -128。
127.0.0.1:6379> get name "Java" # 01001010 从 6 开始对接下来的2位无符号数加 1, 变成 01001011, 75, 对应K 127.0.0.1:6379> BITFIELD name incrby u2 6 1 1) (integer) 3 127.0.0.1:6379> get name "Kava" # 01001011 从 6 开始对接下来的2位无符号数加 1, -- 会溢出, 变成0100100, 72, 对应H 127.0.0.1:6379> BITFIELD name incrby u2 6 1 1) (integer) 0 127.0.0.1:6379> get name "Hava" 127.0.0.1:6379>
也可以修改默认的溢出策略,可以改为fail,表示执行失败:
127.0.0.1:6379> get name "Hava" 127.0.0.1:6379> BITFIELD name overflow fail incrby u2 6 1 1) (integer) 1 127.0.0.1:6379> BITFIELD name overflow fail incrby u2 6 1 1) (integer) 2 127.0.0.1:6379> get name "Java" 127.0.0.1:6379> BITFIELD name overflow fail incrby u2 6 1 1) (integer) 3 127.0.0.1:6379> BITFIELD name overflow fail incrby u2 6 1 1) (nil) 127.0.0.1:6379> get name "Kava" 127.0.0.1:6379>
sat 表示停留在最大/ 最小值 (饱和截断):
127.0.0.1:6379> get name "Kava" 127.0.0.1:6379> BITFIELD name overflow sat incrby u2 6 1 1) (integer) 3 127.0.0.1:6379> BITFIELD name overflow sat incrby u2 6 1 1) (integer) 3 127.0.0.1:6379> BITFIELD name overflow sat incrby u2 6 1 1) (integer) 3 127.0.0.1:6379> BITFIELD name overflow sat incrby u2 6 1 1) (integer) 3 127.0.0.1:6379>
作者:缘木与鱼
链接:https://www.jianshu.com/p/cbdf100f7ec0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。