当前位置: 首页 > 科技观察

本文为大家带来Bitmap在Redis中的精彩使用

时间:2023-03-17 00:55:22 科技观察

在Redis中,我们经常会用到set、get等命令。如果你细心的话,你有没有注意到有几个类似的命令叫做setbit和getbit,它们是用来干什么的?什么是BitMap就是用一个位来表示一个元素对应的值或者状态,其中的key就是对应的元素本身。我们知道8位可以组成一个Byte,所以位图本身会大大节省存储空间。Redis中的BitMapRedis从2.2.0版本开始增加了setbit、getbit、bitcount等几个位图相关的命令。虽然是新命令,但是并没有增加新的数据类型,因为setbit等命令只是set上的扩展。setbit命令引入命令SETBIT键偏移值。复杂度O(1)。设置或清除偏移量处键值(字符串)的位值(仅0或1)。空间占用及首次分配空间所需时间在2010MacBookPro上,offset为2^32-1(分配512MB)耗时~300ms,offset为2^30-1(分配128MB)耗时~80毫秒,偏移量2^28-1(分配32MB)需要约30毫秒,偏移量2^26-1(分配8MB)需要8毫秒。<来自官方文档>。空间使用的近似计算公式为:($offset/8/1024/1024)MB。场景一:用户签到很多网站都提供了签到功能(这里不考虑数据落地),需要展示上个月的签到情况。我们如何使用位图?一句话不同意代码!connect('127.0.0.1');//用户uid$uid=1;//用uid$cacheKey=sprintf("sign_%d",$uid);//登录功能开始的日期$startDate='2017-01-01';//今天的日期$todayDate='2017-01-21';//计算偏移量$startTime=strtotime($开始日期);$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);回声1==$bitStatus?'我今天签到了':'还没有签到';echoPHP_EOL;//计算签到总数echo$redis->bitCount($cacheKey).PHP_EOL;/***计算一定时间内签到的次数*遗憾的是,bitCount提供了start和end参数,但这里指的是字符串的位置,而不是“位”对应的位置。好在我们可以通过get命令把值取出来,自己解析。而且这个值不会太大。上面计算一个用户一年只需要45字节*给我们的网站定个小目标,运行30年,那么一共需要1.31KB(就问你是不是屌丝?)*///这个是错误的计算方法。回显$redis->bitCount($cacheKey,0,20)。PHP_EOL;使用场景2:统计活跃用户使用时间作为cacheKey,然后用户ID偏移,如果当天活跃,则设置为1。那么我应该如何计算某一天/月/年的活跃用户(对于暂且统计时间内只在线一天为活跃),请发送下一条redis命令。命令BITOP操作destkeykey[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($dataas$date=>$uids){$cacheKey=sprintf("stat_%s",$date);foreach($uidsas$uid){$redis->setBit($cacheKey,$uid,1);}}$redis->bitOp('AND','stat','stat_2017-01-10','stat_2017-01-11','stat_2017-01-12')。PHP_EOL;//活跃用户总数:6echo"活跃用户总数:".$redis->bitCount('stat')。PHP_EOL;$redis->bitOp('AND','stat1','stat_2017-01-10','stat_2017-01-11','stat_2017-01-14')。PHP_EOL;//活跃用户总数:2echo"活跃用户总数:".$redis->bitCount('stat1')。PHP_EOL;$redis->bitOp('AND','stat2','stat_2017-01-10','stat_2017-01-11').PHP_EOL;//总活跃用户数:8echo"总活跃用户数:".$redis->bitCount('stat2')。PHP_EOL;假设当前站点有5000W用户,那么一天的数据量约为50000000/8/1024/1024=6MB。使用场景三:用户在线状态前段时间开发了一个项目,对方给我提供了一个查询当前用户是否在线的接口。我不知道对方是怎么做到的。我自己想过。使用位图是一种节省空间且高效的方法。只需要一个密钥,然后偏移用户ID。如果在线则设置为1,如果不在线则设置为0,同上场景,5000W用户只需要6MB空间。//批量设置在线状态$uids=range(1,500000);foreach($uidsas$uid){$redis->setBit('online',$uid,$uid%2);}//获取一个按一个州$uids=range(1,500000);$startTime=微时间(真);foreach($uidsas$uid){echo$redis->getBit('online',$uid).PHP_EOL;}$endTime=microtime(true);//在我的电脑上,需要25秒才能获取50W用户的状态echo"total:".($endTime-$startTime)。"s";/***对于批量获取,上面是一个低效率的方法,其实可以通过get获取值,然后自己计算*具体计算方法改天写,以前写的代码无处可寻。..*/其实BitMap可以在很多场景下使用(当然会有一些限制),思想可以继续传播。