前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >利用 Redis 位运算快速实现签到统计功能

利用 Redis 位运算快速实现签到统计功能

作者头像
overtrue
发布2019-03-19 16:40:55
3.6K0
发布2019-03-19 16:40:55
举报

上篇文章 已经对 Redis 基础命令进行了一个大致的学习,接下来我们就需要解决 Issue“增加用户活跃度统计” 啦!

其实当我看到这个 Issue 的时候,我的第一反应是利用 Mysql 来实现,创建一个签到表,记录用户 ID 和 签到时间,然后统计的时候从数据库中取出来然后聚合计算,完美,哈哈。

但是当看到要求说要用 Redis 位运算的时候,我就在想,为啥呢,仔细想了一哈,发现如果用 Mysql 来实现的话虽然简单粗暴,但是也有弊端,比如我们想要做一些复杂的功能就不是太方便了,或者说不是太高性能了,比如,今天是连续签到的第几天,在一定时间内连续签到了多少天。另外一方面,如果按100万用户量级来计算,一个用户每年可以产生 365条记录,100万用户的所有签到记录那就有点恐怖了,查询计算速度也会越来越慢。

所以毅然选择 Redis,下面给大家介绍一下究竟为啥选择它。

准备

大家知道 Redis 的字符串数据都是以二进制的形式存放的,所以说 Redis 的 Bit 操作非常适合处理这个场景,因为 Bit 的值为 0 或 1,用户是否打卡也可以用 0 或 1 来表示,我们把签到的天数对应到每个字节上,打卡了就是1,没打卡就是0,那么一个用户一年下来的记录就是 365 位的长度,100万用户一年只需要耗费大约 43 M 左右的存储空间就可以了,而且速度贼快,大伙可能会问,这个究竟是怎么计算来的,我们来看一下官方的解释:

在一台 2010MacBook Pro 上,offset 为2^32-1(分配512MB)需要~300ms,offset 为2^30-1(分配128MB)需要~80ms,offset 为2^28-1(分配32 MB)需要~30ms,offset 为2^26-1(分配8MB)需要8ms。 大概的空间占用计算公式是:( offset / 8 / 1024 / 1024 )MB

这里的 offset ,大家姑且当做用户 ID 来看,哈哈。

那么究竟如何去打卡呢,我们可以利用 setbit 命令来实现,setbit 的作用说的直白点就是:在你想要的位置操作字节值,比如说用户 3 在 3月13号 签到了,那么 setbit(20190313, 3 ,1) 就可以实现签到功能了,这里的 offset 就是3,同理,不同的用户不同的日期,改变对应的值就好了。

那么下面我们来实战一下:

实例

1. 实例化一个 redis 连接

代码语言:javascript
复制
 $redis = app('redis.connection');

2. 如何去设计 key 呢?

代码语言:javascript
复制
 $dayKey = 'login:'.\now()->format('Ymd'); // 输出类似:login:20190310

 // 普通写法
 $dayKey = 'login:'.\date('Ymd',\time());

简单粗暴,清晰明了,哈哈。

所以我们大致的格式应该是这样子的:

3. 签到

  • setbit - SETBIT KEY_NAME OFFSET (Time complexity: O(1))
代码语言:javascript
复制
对 key 所储存的字符串值,设置或清除指定偏移量上的位 bit
代码语言:javascript
复制
 $redis->setbit($dayKey, $this->user->id, 1);

可以看到在存储方面不仅耗费内存少,快,而且操作还方便,就这么一句话就搞定了,我当初也以为会是很复杂的操作,哈哈。并且它还有非常低的灵活高效的统计计算成本。

4. 统计一周内的签到数据

  • bitop - BITOP operation destkey key [key ...]
代码语言:javascript
复制
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上
AND : 对一个或多个 key 求逻辑并OR : 对一个或多个 key 求逻辑或XOR : 对一个或多个 key 求逻辑异或NOT : 对给定 key 求逻辑非
代码语言:javascript
复制
 $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');

 echo "当前用户指定天数是否签到:" . $redis->getbit('login:20190311', $this->user->id);
 .....

是不是特别方便快捷的统计查询,哈哈,

结束语

从上面的例子中大家可以看到不管在存储上面还是在统计计算上面,位运算都比 mysql 的方式好太多。

至此,一个简单的签到统计功能就已经实现了,大家可以根据自己的需求扩展,不当的地方欢迎大家指正,哈哈。

相关链接

  1. 一刻社区源码:https://github.com/overtrue/api.yike.io
  2. reids 中 bitmap 的妙用:https://segmentfault.com/a/1190000008188655
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 假装我会写代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 准备
  • 实例
  • 结束语
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档