在Redis中我们经常用到set,get等命令,细心的你有没有发现,还有几个相似的命令叫setbit,getbit,它们是用来干嘛的?

BitMap是什么

就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。

Redis中的BitMap

Redis从2.2.0版本开始新增了setbit,getbit,bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。

setbit命令介绍

指令 SETBIT key offset value

复杂度 O(1)

设置或者清空key的value(字符串)在offset处的bit值(只能只0或者1)。

空间占用、以及第一次分配空间需要的时间

在一台2010MacBook Pro上,offset为2^32-1(分配512MB)需要~300ms,offset为2^30-1(分配128MB)需要~80ms,offset为2^28-1(分配32MB)需要~30ms,offset为2^26-1(分配8MB)需要8ms。

大概的空间占用计算公式是:($offset/8/1024/1024)MB

使用场景一:用户签到

很多网站都提供了签到功能(这里不考虑数据落地事宜),并且需要展示最近一个月的签到情况,如果使用bitmap我们怎么做?一言不合亮代码!

$redis = new Redis();

$redis->connect('127.0.0.1');

//用户uid

$uid = 1;

//记录有uid的key

$cacheKey = sprintf("sign_%d", $uid);

//开始有签到功能的日期

$startDate = '2017-01-01';

//今天的日期

$todayDate = '2017-01-21';

//计算offset

$startTime = strtotime($startDate);

$todayTime = strtotime($todayDate);

$offset = floor(($todayTime - $startTime) / 86400);

echo "今天是第{$offset}天" . PHP_EOL;

//签到

//一年一个用户会占用多少空间呢?大约365/8=45.625个字节,好小,有木有被惊呆?

$redis->setBit($cacheKey, $offset, 1);

//查询签到情况

$bitStatus = $redis->getBit($cacheKey, $offset);

echo 1 == $bitStatus ? '今天已经签到啦' : '还没有签到呢';

echo PHP_EOL;

//计算总签到次数

echo $redis->bitCount($cacheKey) . PHP_EOL;

/**

* 计算某段时间内的签到次数

* 很不幸啊,bitCount虽然提供了start和end参数,但是这个说的是字符串的位置,而不是对应"位"的位置

* 幸运的是我们可以通过get命令将value取出来,自己解析。并且这个value不会太大,上面计算过一年一个用户只需要45个字节

* 给我们的网站定一个小目标,运行30年,那么一共需要1.31KB(就问你屌不屌?)

*/

//这是个错误的计算方式

echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;

使用场景二:统计活跃用户

使用时间作为cacheKey,然后用户ID为offset,如果当日活跃过就设置为1

那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个redis的命令

命令 BITOP operation destkey key [key ...]

说明:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。

说明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数

//日期对应的活跃用户

$data = array(

'2017-01-10' => array(1,2,3,4,5,6,7,8,9,10),

'2017-01-11' => array(1,2,3,4,5,6,7,8),

'2017-01-12' => array(1,2,3,4,5,6),

'2017-01-13' => array(1,2,3,4),

'2017-01-14' => array(1,2)

);

//批量设置活跃状态

foreach($data as $date=>$uids) {

$cacheKey = sprintf("stat_%s", $date);

foreach($uids as $uid) {

$redis->setBit($cacheKey, $uid, 1);

}

}

$redis->bitOp('AND', 'stat', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-12') . PHP_EOL;

//总活跃用户:6

echo "总活跃用户:" . $redis->bitCount('stat') . PHP_EOL;

$redis->bitOp('AND', 'stat1', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-14') . PHP_EOL;

//总活跃用户:2

echo "总活跃用户:" . $redis->bitCount('stat1') . PHP_EOL;

$redis->bitOp('AND', 'stat2', 'stat_2017-01-10', 'stat_2017-01-11') . PHP_EOL;

//总活跃用户:8

echo "总活跃用户:" . $redis->bitCount('stat2') . PHP_EOL;

假设当前站点有5000W用户,那么一天的数据大约为50000000/8/1024/1024=6MB

使用场景三:用户在线状态

前段时间开发一个项目,对方给我提供了一个查询当前用户是否在线的接口。不了解对方是怎么做的,自己考虑了一下,使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户ID为offset,如果在线就设置为1,不在线就设置为0,和上面的场景一样,5000W用户只需要6MB的空间。

//批量设置在线状态

$uids = range(1, 500000);

foreach($uids as $uid) {

$redis->setBit('online', $uid, $uid % 2);

}

//一个一个获取状态

$uids = range(1, 500000);

$startTime = microtime(true);

foreach($uids as $uid) {

echo $redis->getBit('online', $uid) . PHP_EOL;

}

$endTime = microtime(true);

//在我的电脑上,获取50W个用户的状态需要25秒

echo "total:" . ($endTime - $startTime) . "s";

/**

* 对于批量的获取,上面是一种效率低的办法,实际可以通过get获取到value,然后自己计算

* 具体计算方法改天再写吧,之前写的代码找不见了。。。

*/

其实BitMap可以运用的场景很多很多(当然也会受到一些限制),思维可以继续扩散~欢迎小伙伴给我留言探讨~

关注公众号,一起学习成长~

redis 中一个字段 修改map_Redis中bitmap的妙用相关推荐

  1. redis 中一个字段 修改map_Redis bitmap 位图 从入门到精通 基础 实战 妙用

    1.bitmap介绍 位图不是真正的数据类型,它是定义在字符串类型中,一个字符串类型的值最多能存储512M字节的内容 位上限:2^(9(512)+10(1024)+10(1024)+3(8b=1B)) ...

  2. redis 中一个字段 修改map_Redis 几种数据类型及应用场景

    Redis支持5种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合).何时使用Redis呢? 先通过一张图了解下Redis ...

  3. redis 中一个字段 修改map_CTO 指名点姓让我带头冲锋,熬了一个通宵,终于把Redis中7千万个Key删完了...

    由于有一条业务线不理想,高层决定下架业务.对于我们技术团队而言,其对应的所有服务器资源和其他相关资源都要释放. 释放了 8 台应用服务器:1 台 ES 服务器:删除分布式定时任务中心相关的业务任务:备 ...

  4. mysql复制一个字段值到另一个字段,MySQL怎么把表中一个字段数据复制到另外一个表的某个字段下...

    点击查看MySQL怎么把表中一个字段数据复制到另外一个表的某个字段下具体信息 答:update b set tel =(select mobile from a where a.id=b.aid) 注 ...

  5. 集合中的实体类根据一个字段合并_JAVA中序列化与反序列化

    一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化: 把字节序列恢复为对象的过程称为对象的反序列化. 说人话:就是JAVA对象需要保存或通过网络传输必须保存为二进制才行,所以就需要 ...

  6. MathType中如何批量修改论文中的公式字体和大小

    原文地址:http://www.mathtype.cn/jiqiao/piliang-xiugai-zitidaxiao.html MathType应用在论文中时,有时会因为排版问题批量修改公式字体和 ...

  7. java mysql tinyint_Msq 中tinyint字段对应 java中哪个类型?

    展开全部 MySql 中的tinyint(1)的使用 在MySql中如何定义像Java中类型的Boolean类型数据..其实,mysql中 是没有直接定义成Boolean这种数据类型. 它只能定义成 ...

  8. mysql 中 一个汉字吗_MySQL 中一个汉字占多少存储?

    MySQL 中一个汉字占多少存储? 1. 一个汉字占多少长度与编码有关: UTF8: 一个汉字 = 3 个字节 GBK: 一个汉字 = 2 个字节 2.varchar(n)能存储几个汉字? varch ...

  9. mysql 设置一个字段为null_MySQL中,为什么要设置NOT NULL?

    有情怀,有干货,微信搜索[荒古传说]关注这个不一样的程序员. 平时开发过程中,MySQL 字段的一般都会设置为NOT NULL,你有想过为什么吗??? 原因如下: 1.空值("") ...

最新文章

  1. Grid SearchCV(网格搜索)与RandomizedSearchCV (随机搜索) 贴近实践的
  2. matlab 最大熵谱估计,最大熵原理
  3. MATLAB实战应用案例:欧拉法、改进欧拉法、ode45求解微分方程实例
  4. python return返回值在计算机内的存储形式_Python如何执行存储过程,获取存储过程返回值...
  5. Hook API (C++)
  6. python 人脸关键点检测_opencv+python+dlib人脸关键点检测、实时检测
  7. byte[] 转Hex String
  8. FMX控件演示(FireMonkey ControlsDemo)
  9. 异名一文带你读懂Chrome小恐龙跑酷!
  10. k线图的分析小技巧以及买入卖出信号
  11. xposed 框架学习
  12. 黑马程序员————高新技术————内省(了解JavaBean)
  13. selenium firefox 提取qq空间相册链接
  14. cocos studio
  15. 混合开发Hybrid App为何成为热门?
  16. 打造高效敏捷的研发独立团 (2009年培训心得)
  17. github中的LICENSE是什么
  18. 实例:【基于机器学习的NBA球员信息数据分析与可视化】
  19. 可免费使用的pdf转换成ppt软件
  20. 笔记本卸载VMware后出现键盘和触摸板用不了

热门文章

  1. 通过日志分析mysql访问量,Mysql 慢查询和慢查询日志分析
  2. hdu2112最短路径
  3. 【初级02】java JVM核心技术(2)开发工具和GC策略
  4. 百度服务器临时文件多久一删,百度站长提醒:11月9日前尽快删除超出站点配额的历史sitemap文件...
  5. ModuleNotFoundError: No module named ‘matplotlib‘ 解决办法
  6. MySQL反斜杠 ‘\\‘ 插入数据库丢失
  7. Ubuntu18.04安装显卡驱动
  8. C++使用using与typedef定义别名
  9. arm和thumb指令模式
  10. android7.0 ActivityManagerService(AMS)启动流程