redis位图
<?php
function frstr($str){
return str_pad($str,8,'0',STR_PAD_LEFT);
}
$php='';
$p= frstr(decbin(ord('p')));
$h= frstr(decbin(ord('h')));
$php=$p.$h.$p;
echo $php;
echo PHP_EOL;
$len=strlen($php);
$index=[];
for($i=0;$i<$len;$i++){
if($php[$i]==1){
$index[]=$i;
}
}
print_r($index);
01110000,01101000,01110000
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 9
[4] => 10
[5] => 12
[6] => 17
[7] => 18
[8] => 19
)
01110000,01101000,01110000
1,2,3,9,10,12,17,18,19 共9位需设置为1
127.0.0.1:0>get p
null
127.0.0.1:0>setbit p 1 1
"0"
127.0.0.1:0>setbit p 2 1
"0"
127.0.0.1:0>setbit p 3 1
"0"
127.0.0.1:0>get p
"p"
127.0.0.1:0>setbit p 9 1
"0"
127.0.0.1:0>setbit p 10 1
"0"
127.0.0.1:0>setbit p 12 1
"0"
127.0.0.1:0>setbit p 17 1
"0"
127.0.0.1:0>setbit p 18 1
"0"
127.0.0.1:0>setbit p 19 1
"0"
127.0.0.1:0>get p
"php"
统计和查找
Redis 提供了位图统计指令 bitcount 和位图查找指令 bitpos- bitcount 用来统计指定位置范围内 1 的个数
- bitpos 用来查找指定范围内出现的第一个 0 或 1
bitcount,bitpos
127.0.0.1:0>bitcount p
"9"
127.0.0.1:0>bitcount p 0 0 #第一个字符中1的个数
"3"
127.0.0.1:0>bitcount p 0 1 #前2个字符中1的个数
"6"
127.0.0.1:0>bitpos p 0 #第一个0位
"0"
127.0.0.1:0>bitpos p 1 #第一个1位
"1"
127.0.0.1:0>bitpos p 1 1 1 #从第二个字符算起第一个1位
"9"
127.0.0.1:0>bitpos p 1 2 2 # 从第三个字符算起,第一个 1 位
"17"
bitfield
bitfield(3.2版本之后) 有三个子指令,分别是 get/set/incrby,它们都可以对指定位片段进行读写,但是最多只能处理 64 个连续的位,如果超过 64 位,就得使用多个子指令,bitfield 可以一次执行多个子指令
php
01110000,01101000,01110000
127.0.0.1:6379> get p
"php"
127.0.0.1:6379> bitfield p get u4 0 # 从第0个位开始取4个位 即为 0111 结果是无符号数 (u)
1) (integer) 7
127.0.0.1:6379> bitfield p get u3 2 #从第2个位开始取3个位 即为110 结果是无符号数 (u)
1) (integer) 6
127.0.0.1:6379> bitfield p get i4 0 #从第0个位开始取4个位 即为0111 结果是有符号数 (i)
1) (integer) 7
127.0.0.1:6379> bitfield p get i3 2 #从第2个位开始取3个 即为110 结果是有符号数 (i)
1) (integer) -2
127.0.0.1:6379> bitfield p get u4 0 get u3 2 get i4 0 get i3 2
1) (integer) 7
2) (integer) 6
3) (integer) 7
4) (integer) -2
所谓有符号数是指获取的位数组中第一个位是符号位,剩下的才是值
如果第一位是 1,那就是负数
无符号数表示非负数,没有符号位,获取的位数组全部都是值。有符号数最多可以获取 64 位,无符号数只能获取 63 位 (因为 Redis 协议中的 integer 是有符号数,最大 64 位,不能传递 64 位无符号值)
如果超出位数限制,Redis 就会告诉你参数错误
01110000,01101000,01110000
把第二个h这只为p
只需要把第11位由0->1,第12位由1->0即可
setbit p 11 1
setbit p 12 0
127.0.0.1:6379> setbit p 11 1
(integer) 0
127.0.0.1:6379> setbit p 12 0
(integer) 1
127.0.0.1:6379> get p
"ppp"
127.0.0.1:6379> set p php
OK
127.0.0.1:6379> bitfield p set u8 8 97 #从第 8 个位开始,将接下来的 8 个位用无符号数 97 替换
1) (integer) 104
127.0.0.1:6379> get p
"pap"
incrby
它用来对指定范围的位进行自增操作自增有可能出现溢出
如果增加了正数,会出现上溢,如果增加的是负数,就会出现下溢出
Redis 默认的处理是折返。如果出现了溢出,就将溢出的符号位丢掉
如果是 8 位无符号数 255,加 1 后就会溢出,会全部变零。如果是 8 位有符号数 127,加 1 后就会溢出变成 -128
bitfield p incrby u7 1 1
127.0.0.1:6379> set p php
OK
127.0.0.1:6379> bitfield p incrby u7 1 1 # 从第7位开始,对接下来的1位无符号数 +1
1) (integer) 113
127.0.0.1:6379> get p
"qhp"
127.0.0.1:6379> bitfield p incrby u7 1 1
1) (integer) 114 #r 的ascii码 为114
127.0.0.1:6379> get p
"rhp"
127.0.0.1:6379> set p php
OK
127.0.0.1:6379> bitfield p incrby u1 1 1
1) (integer) 0
127.0.0.1:6379> get p
"0hp"
127.0.0.1:6379> bitfield p incrby u1 1 1
1) (integer) 1
127.0.0.1:6379> get p
"php"
bitfield 指令提供了溢出策略子指令
overflow,用户可以选择溢出行为,默认是折返 (wrap),还可以选择失败 (fail) 报错不执行,以及饱和截断 (sat),超过了范围就停留在最大最小值
overflow 指令只影响接下来的第一条指令,这条指令执行完后溢出策略会变成默认值折返 (wrap)
饱和截断 SAT
p的二进制0111 0000
127.0.0.1:6379> get p
"p"
127.0.0.1:6379> bitfield p overflow sat incrby u4 1 1
1) (integer) 15
127.0.0.1:6379> get p
"x"
127.0.0.1:6379> bitfield p overflow sat incrby u4 1 1
1) (integer) 15
127.0.0.1:6379> get p
"x"
127.0.0.1:6379> bitfield p overflow sat incrby u4 1 1 ## 保持最大值
1) (integer) 15
127.0.0.1:6379> get p
"x"
对
0111 0000
执行bitfield p overflow sat incrby u4 1 1
后变为
0111 1000
继续bitfield p overflow sat incrby u4 1 1
因为采取了截断 (sat),超过了范围就停留在最大最小值
失败不执行 FAIL SAT
127.0.0.1:6379> set p p
OK
127.0.0.1:6379> bitfield p overflow fail incrby u5 1 1
1) (integer) 29
127.0.0.1:6379> get p
"t"
127.0.0.1:6379> bitfield p overflow fail incrby u5 1 1
1) (integer) 30
127.0.0.1:6379> get p
"x"
127.0.0.1:6379> bitfield p overflow fail incrby u5 1 1
1) (integer) 31
127.0.0.1:6379> get p
"|"
127.0.0.1:6379> bitfield p overflow fail incrby u5 1 1
1) (nil)
127.0.0.1:6379> get p
"|"
127.0.0.1:6379>
过程分析
对
0111 0000
执行bitfield p overflow fail incrby u5 1 1
变为
0111 0100
ascii码为116 即为字符t然后继续执行
bitfield p overflow fail incrby u5 1 1
0111 1000
ascii为120即为字符 x然后继续执行
bitfield p overflow fail incrby u5 1 1
变为
0111 1100
ascii 124即为字符 |再继续执行就会溢出,所以保留当前的最大值