Redis的基本使用(三) Bit的操作
Redis的基本使用(三) Bit的操作
1. 场景:
记录用户一年的签到记录,如果用String存储,需要使用365个key/value,操作起来麻烦 ,通过位图可以有效的简化这个操作。 它的统计很简单:如签到记为1,没有签到记为0,每天的记录占一个位:
1011011111 这样大概需要46个字节(365天占365位,一个字节8位),可以有效的节省存储空间。如果有一天想要统计用户一共签到的天数,只需要统计1的位数即可。
对于位图的操作,可以直接操作对应的字符串的(get/set),可以直接操作位(getbit/setbit).
2. 基本操作
Redis的基本操作可以归为两大类:
2.1. 零存整取
例如存储一个java字符串:(存储时是一个一个的,取出的时候按字符串一次取出)
字符 | ASCII | 二进制 |
---|---|---|
J | 74 | 01001010 |
a | 97 | 01100001 |
v | 118 | 01110110 |
接下来去存储:

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>