Redis中位图Bitmaps的使用(签到功能的实现)

Bitmaps并不属于Redis中数据结构的一种,它其实是使用了字符串类型,是set、get等一系列字符串操作的一种扩展,与其不同的是,它提供的是位级别的操作,从这个角度看,我们也可以把它当成是一种位数组、位向量结构。当我们需要存取一些boolean类型的信息时,Bitmap是一个非常不错的选择,在节省内存的同时也拥有很好的存取速度(getbit/setbit操作时间复杂度为O(1))。
假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户A上线了多少天,用户B上
线了多少天
,诸如此类,以此作为数据,从而决定让哪些用户参加beta测试等活动——这个模式可以使
用SETBIT和BITCOUNT来实现。

性能

以上线次数统计例子,即使运行10年,占用的空间也只是每个用户10*365比特位(bit),也即是每个
用户456字节。对于这种大小的数据来说,BITCOUNT的处理速度就像GET和INCR这种O(1)复杂度的
操作一样快。

 

1 常用命令
SETBIT key offset value   可用版本 >= 2.2.0

设置或者清空key的value(字符串)在offset处的bit值。当key不存在的时候,将新建字符串value。参数offset需要大于等于0,并且小于232(限制Bitmap大小为512MB)。没有setbit的位会默认设定为0


GETBIT key offset 可用版本 >= 2.2.0
返回key对应的string在offset处的bit值。当offset超出了字符串长度或key不存在时,返回0。

BITCOUNT key [start end]   可用版本 >= 2.6.0
时间复杂度: O(N)
统计字符串被设置为1的bit数。需要注意的是,这里的start和end并不是位偏移,而是以字节(8位)为单位来偏移的,比如BITCOUNT foo 0 1是统计key为foo的字符串中第一个到第二个字节中bit为1的总数。

BITPOS key bit [start] [end]  可用版本>= 2.8.7
时间复杂度: O(N),其中 N 为位图包含的二进制位数量
返回位图中第一个值为 bit 的二进制位的位置。
在默认情况下, 命令将检测整个位图, 但用户也可以通过可选的 start 参数和 end 参数指定要检测的范围。

BITOP operation destkey key [key ...]
起始版本:2.6.0  复杂度是O(N)

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

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

  • BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
  • BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
  • BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
  • BITOP NOT destkey srckey,对给定 key 求逻辑非,并将结果保存到 destkey 。

除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。
执行结果将始终保持到destkey里面。

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
可用版本: >=3.2.0
时间复杂度: O(1)用于指定的每个子命令

bitfield 有三个子指令,分别是 get/set/incrby,它们都可以对指定位片段进行读写,但是最多只能处理 64 个连续的位,如果超过 64 位,就得使用多个子指令,bitfield 可以一次执行多个子指令。

eg :   命令将位偏移量为100的8位有符号整数加1,并在位偏移量0处获取4位无符号整数的值:
> BITFIELD mykey INCRBY i5 100 1 GET u4 0
1) (integer) 1
2) (integer) 0

 

2 使用Bitmaps实现统计活跃用户数

Bitmap常见的应用场景之一就是用户签到了,在这里,我们以日期作为key,以用户ID作为位偏移(也就是索引),存储用户的签到信息(1为签到,0为未签到)。不过这个方法也是有前置条件的,那就是 userid 是整数连续的,并且活跃占比较高,否则可能得不偿失。

Redis中位图Bitmaps的使用(签到功能的实现)_第1张图片

用户签到                                     SETBIT 20190602  userId  1
用户是否签到                              GETBIT 20190602  userId
某天用户的活跃数:                      BITCOUNT  20190602
用户某个时间段签到次数            通过遍历获取GETBIT 20190602  userId值
用户连续签到天数                       get userId   设置过期日期为第二天 
获取当天第一个签到到的用户     BITPOS 20190602 1

统计: 

$redis->bitop('AND', 'threeAnd', 'login:20190311', 'login:20190312', 'login:20190313');
echo "连续三天都签到的用户数量:" . $redis->bitCount('threeAnd');

$redis->bitop('OR', 'threeOr', 'login:20190311', 'login:20190312', 'login:20190313');
echo "三天中签到用户数量(有一天签也算签了):" . $redis->bitCount('threeOr');

$redis->bitop('AND', 'monthActivities'', $redis->keys('login:201903*'));
echo "连续一个月签到用户数量:" . $redis->bitCount('monthActivities');

 

3使用Bitmaps 用户在线状态

考虑到每月初需要重置连续签到次数,最简单的方式是按用户每月存一条签到数据(也可以每年存一条数据)。Key的格式为u:sign:uid:yyyyMM,Value则采用长度为4个字节(32位)的位图(最大月份只有31天)。位图的每一位代表一天的签到,1表示已签,0表示未签。

#用户2月17号签到
SETBIT u:sign:1000:201902 16 1 偏移量是从0开始,所以要把17减1
 
#检查2月17号是否签到
GETBIT u:sign:1000:201902 16   偏移量是从0开始,所以要把17减1
 
#统计2月份的累计签到次数
BITCOUNT u:sign:1000:201902

#统计2月份的连续签到次数(暂定),并设置有效期为第二天
set u:signcount:1000:201902
 
#获取2月份前28天的签到数据
BITFIELD u:sign:1000:201902 get u28 0
 
#获取2月份首次签到的日期
BITPOS u:sign:1000:201902 1  返回的首次签到的偏移量,加上1即为当月的某一天

参考: https://blog.csdn.net/CrazyLai1996/article/details/85220910

         https://www.cnblogs.com/liujiduo/p/10396020.html

         https://blog.csdn.net/HUXU981598436/article/details/88191152

          https://learnku.com/articles/25181

你可能感兴趣的:(redis)