1 示例
用户一年的签到记录,如果你用String类型来存储,那则需要365个 key/value来存储,操作起来麻烦。通过位图可以有效的简化这个操作。
它的统计很简单:
01111000111(0没签到,1签到)(每一个占1比特,8比特就是1字节)
每天的记录占一个位,365天就是365个位,大概46个字节,这样可以有效的节省存储空间,如果有一天想要通过用户一共签到了多少天,统计1的个数即可。
对于位图的操作,可以直接操作对应的字符串(get/set),可以直接操作位(getbit/setbit)
2 操作
Redis的基础操作可以归为两大类:
2.1 零存整取
存的时候是位,但获取的时候是直接获取字符串
例如存储一个java字符串
字符 | ASCII | 二进制 |
---|---|---|
J | 74 | 01001010 |
a | 97 | 01100001 |
v | 118 | 01110110 |
74=64+8+2
97=64+32+1
118=64+32+16+4+2
接下来去存储:
127.0.0.1:6379> setbit n 1 1
(integer) 0
127.0.0.1:6379> setbit n 4 1
(integer) 0
127.0.0.1:6379> setbit n 6 1
(integer) 0
127.0.0.1:6379> get n
"J"
127.0.0.1:6379> setbit n 9 1
(integer) 0
127.0.0.1:6379> setbit n 10 1
(integer) 0
127.0.0.1:6379> setbit n 15 1
(integer) 0
127.0.0.1:6379> get n
"Ja"
127.0.0.1:6379> setbit n 17 1
(integer) 0
127.0.0.1:6379> setbit n 18 1
(integer) 0
127.0.0.1:6379> setbit n 19 1
(integer) 0
127.0.0.1:6379> setbit n 21 1
(integer) 0
127.0.0.1:6379> setbit n 22 1
(integer) 0
127.0.0.1:6379> get n
"Jav"
127.0.0.1:6379> setbit n 25 1
(integer) 0
127.0.0.1:6379> setbit n 26 1
(integer) 0
127.0.0.1:6379> setbit n 31 1
(integer) 0
127.0.0.1:6379> get n
"Java"
0~7 第一个字符
8~15 第二个字符
16~23 第三个字符
24~31 第四个字符
2.2 整存零取
存的时候是一个字符串,但是通过位操作获取字符串中位的值
127.0.0.1:6379> set k1 www
OK
127.0.0.1:6379> get k1
"www"
127.0.0.1:6379> getbit k1 1
(integer) 1
127.0.0.1:6379> getbit k1 3
(integer) 1
127.0.0.1:6379> getbit k1 23
(integer) 1
127.0.0.1:6379> getbit k1 24
(integer) 0
2.3 统计
例如签到记录:
01111000111
1 表示签到的天,0表示没签到,统计总的签到天数:
可以使用bitcount(统计字符串里面1的个数)
127.0.0.1:6379> set name java
OK
127.0.0.1:6379> bitcount name
(integer) 15
bitcount中,可以统计里面的起始位置,但是注意,这个起始位置是指字符串的起始位置而不是bit的起始位置。
127.0.0.1:6379> bitcount name 0 0
(integer) 4
127.0.0.1:6379> bitcount name 0 1
(integer) 7
bitpos:统计(查找指定范围内出现的第一次0或1的位置),这个命令中的起始和结束位置都是字符索引,不是bit索引,一定要注意。
127.0.0.1:6379> bitpos name 1
(integer) 1
127.0.0.1:6379> bitpos name 0
(integer) 0
127.0.0.1:6379> bitpos name 0 1 1
(integer) 8
查找从1到1中第一个0的位置,即a里面第一个出现0的位置
2.4 Bit批处理
在Redis3.2之后,新加的一个功能叫做bitfield
,可以对bit进行批量操作。
例如:
bitfield name get u4 0
表示获取name中的位,从0开始获取,获取4个位,返回一个无符号数字(u表示无符号)。
(i表示有符号):有符号的话,第一个符合就表示符号位,1表示一个负数
01000011中的4位就是0100=4
127.0.0.1:6379> BITFIELD name get u4 0
1) (integer) 6
01001010 J:u4 1 (1001) 等于8+1=9
J:i4 1 等于-8+1=-7
bitfield也可以一次执行多个操作
- get:
127.0.0.1:6379> bitfield name get u4 0 get i4 0 get u4 1 get i4 1
1) (integer) 6
2) (integer) 6
3) (integer) 13
4) (integer) -3
- set:
127.0.0.1:6379> bitfield name set u8 8 98
1) (integer) 97
127.0.0.1:6379> get name
"jbva"
从第8位开始,把之前的无符号的替代掉,就是用b替代a。
用无符号的98转成的8位二进制数字,代替从第8位开始接下来的8位数字。
- incrby:
对指定范围进行自增操作,自增操作可能会出现溢出,既可能向上溢出,也可能是向下溢出,Redis中对于溢出的处理方案是折返。8位无符号数255加1溢出变为0;8位有符号数127,加1变为-128
127.0.0.1:6379> bitfield name incrby u2 6 1
1) (integer) 3
127.0.0.1:6379> get name
"Kava"
127.0.0.1:6379> bitfield name incrby u2 6 1
1) (integer) 0
127.0.0.1:6379> get name
"Hava"
Java 的前8位:01001010
从第6位开始,选2位加一,10变11
继续加一,溢出,11变00
也可以修改默认的溢出策略,可以改为fail,表示执行失败
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> 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"
- sat表示留在最大/最小值
定义规则停留在最大或者最小值
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