<?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即为字符 |

再继续执行就会溢出,所以保留当前的最大值

redis位图的更多相关文章

  1. 基于Redis位图实现系统用户登录统计

    项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小.具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进! 1. 需求 1. 实现记录用户哪天进行了登录,每天只记录是否登录过,重 ...

  2. Redis位图实现用户签到功能

    场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满 ...

  3. 基于Redis位图实现用户签到功能

    场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满 ...

  4. Redis位图法记录在线用户的状态

    Redis位图法记录在线用户的状态 位图 Redis官方文档对于位图的介绍如下: 位图不是一个真实的数据类型,而是定义在字符串类型上的面向位的操作的集合.由于字符串类型是二进制安全的二进制大对象,并且 ...

  5. 巧用redis位图存储亿级数据与访问 - 简书

    原文:巧用redis位图存储亿级数据与访问 - 简书 业务背景 现有一个业务需求,需要从一批很大的用户活跃数据(2亿+)中判断用户是否是活跃用户.由于此数据是基于用户的各种行为日志清洗才能得到,数据部 ...

  6. 巧用redis位图存储亿级数据与访问

    业务背景 现有一个业务需求,需要从一批很大的用户活跃数据(2亿+)中判断用户是否是活跃用户.由于此数据是基于用户的各种行为日志清洗才能得到,数据部门不能提供实时接口,只能提供包含用户及是否活跃的指定格 ...

  7. redis位图巧用,节约内存

    最近要做一个圣诞抽奖活动,需要记录每天用户签到的记录,以前一般都是用普通的字符串数据类型,每个用户的签到用一个 key // 用户10在活动第一天的签到key为record:1:10 $key = & ...

  8. redis位图命令

    概述 redis 的位图就是01的数据格式,redis 主要做有写入,读取和统计.位图相关的命令 : 其中set和get就是 read 和writer , bitcount 统计相关,bitop 是对 ...

  9. redis位图(bitmap)常用命令的解析

    描述   bitmap是redis封装的用于针对位(bit)的操作,其特点是计算效率高,占用空间少,常被用来统计用户签到.登录等场景 常用命令及解析 常用命令 setbit key offset va ...

随机推荐

  1. 简单实现Ubuntu16.04 + caffe2 + CUDA9.0 + cuDNN8.0

    在Ubuntu16.04 CUDA9.0 cuDNN8.0的环境下安装caffe2 本博客比较简单,cuda9.0 cudnn8.0部分请看上一篇博客,其中详细讲了: 如何安装驱动 安装cuda 安装 ...

  2. SDN前瞻 该来的来了!SDN 软件定义网络

    SDDC:Software Defined Data Center 软件定义数据中心,全数据中心软件化. 在我们接触SDN概念之前,服务器虚拟化,软件虚拟化技术已经是非常成熟了.如果网络能够被虚拟化, ...

  3. IDEA上面如何添加创建的文件类型

    首先在IDEA --> new 一个文件的时候 以 创建vue文件为例子,来添加vue文件类型文件 选项 edit file templates 添加 这样就OK了 转载需注明出处,谢谢! au ...

  4. Linux 实现软件可视化安装(VNC)

    (1)光盘挂载或者配置yum源,如果是在虚拟机上练习,可以使用如下命令进行光盘挂载: sudo mkdir /mnt/cdrom sudo mount /dev/cdrom /mnt/cdrom 但是 ...

  5. C++ 构造函数后加冒号

    其实冒号后的内容是初始化成员列表,一般有三种情况: 1.对含有对象成员的对象进行初始化,例如, 类line有两个私有对象成员startpoint.endpoint,line的构造函数写成: line( ...

  6. Linux网络编程--洪水攻击详解

    洪水攻击详解 ①注解:洪水攻击(FLOOD ATTACK)是指利用计算机网络技术向目标主机发送大量无用的数据报文,使得目标主机忙于处理无用的数据报文而无法提供正常服务的网络行为. 主要原理:利用了网络 ...

  7. hdu1517找规律

    挺像巴什博弈的,直接递推就能找到规律了,从2开始到9,s win,10到18,o win,18到162,s win,一直向下推进 #include<map> #include<set ...

  8. java实现的18位身份证格式验证算法

    公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成.排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码.1.地址码表示编码对象常住户口所在县(市. ...

  9. 快速切题 sgu134.Centroid 树形dp

    134. Centroid time limit per test: 0.25 sec. memory limit per test: 4096 KB You are given an undirec ...

  10. 3d世界是怎样呈现到屏幕上的

    要把一个3d物体呈现在屏幕上,要经过一系列的步骤. 描述3d世界 把3d世界绘制在二维屏幕上 如何描述一个3D世界? 数学家早就给出了3D世界的模型,我们日常最熟悉的3维坐标系就是一个欧几里得空间(线 ...