用Redis实现签到功能
一、场景
在很多时候我们会遇到用户签到的场景,每天用户进入应用时,需要获取用户当天的签到状态,如果没签到,用户可以进行签到,并且得到相关的奖励。我们可能需要每天的签到情况,必要的时候可能还需要统计一下每天用户签到人数。
我们用Redis的Set数据结构可以轻松实现这个功能——以日期为key,以用户ID(对应着数据库的primary id)组成的集合为value,每当需要查询某个用户的签到状态时,只需要使用命令SISMEMBER key member就可以轻易得到想要的结果;用户签到时,使用命令SADD key member把用户ID添加到相应的日期中;统计某天用户的签到人数,可以用命令SCARD key。
以上的做法操作简便,易于理解,但是本篇要介绍的是另一种做法,使用Redis的位操作(bitmap)。
我们都知道数据在机器上存储的最小单元是位(bit),1位可以存储0和1两种状态。这里的场景需要存储正是签到和未签到两种状态,因此一个用户只需要占用1位,也就是用位操作比用集合操作要省很多空间,下面先说一下位操作的方式,最后会给出两种方式的内存占用对比。
Redis提供了一组位操作相关的指令,这里我们关注下面三个:
- BITCOUNT key [start end]
返回key的开始位置start到结束位置end之间位值为1的数量,如果key不存在,返回0;如果不指定start和end,返回整个key的位值为1的数量。
- GETBIT key offset
返回key的第offset位的位值。
- SETBIT key offset value
把key的第offset位的值设置为value,value只能是0或1。
说明一下Redis的位操作的偏移量(offset)是从0开始算起的,而且最左边那位是第0位,这与数值的二进制有点不同(数值的二进制最右边那位是第0位)。
二、解决方案
有了以上两组操作之后,再回到我们的场景,这里假定我们有500w注册用户,日活又主动签到的用户只有30w,新用户的活跃度更高。如果使用redis的Set的操作,那么我们每天需要存储的数据就是这30w用户的id,一般来说,新注册的用户的活跃度会比旧用户的活跃度要高,为了方便测试,我们假定每天活跃的用户就是id最大的30w用户。下面是两种方案的具体操作:
2.1、使用Set存储数据
先准备30w条redis指令并且写到一个data.txt文件中,格式如下:
SADD sign_in_20200113 4700001
SADD sign_in_20200113 4700002
SADD sign_in_20200113 4700003
SADD sign_in_20200113 4700004
SADD sign_in_20200113 4700005
...
然后通过redis的管道命令来把数据写到redis:
cat data.txt | redis-cli --pipe
完成后可以看一下数据是否成功写到redis中:
127.0.0.1:6379> scard sign_in_20200113
(integer) 300000
指定的key已经有30w个用户签到,同时用info命令查看一下这时redis的占用内存:
# Memory
used_memory:21604936
used_memory_human:20.60M
占用的内存大概是20M。
然后我们需要查询一个用户的签到状态和用户签到都非常方便。
2.2、使用bitmap存储数据
接下来我们再用bitmap进行操作,同样我们准备好相关的redis指令,如下:
SETBIT sign_in_20200113 4700001 1
SETBIT sign_in_20200113 4700002 1
SETBIT sign_in_20200113 4700003 1
SETBIT sign_in_20200113 4700004 1
SETBIT sign_in_20200113 4700005 1
...
完成后我们可以通过bitcount命令查看一下签到人数:
127.0.0.1:6379> bitcount sign_in_20200113
(integer) 300000
这时再看一下占用内存的情况:
# Memory
used_memory:2088200
used_memory_human:1.99M
只占了大约2M,和使用Set的方式相差了10倍!
三、方案对比
- 使用Set的方式所占用的内存只与数量相关,和存储哪些id无关
- 使用bitmap的方式所占用的内存与数量没有绝对的关系,而是与最高位有关。比如假设id为500w的用户签到了,那么从1号用户到4999999号用户不管是否签到,所占的内存都是500w个bit,这也是bitmap的最坏情况,假如上述场景是1号用户到30w号用户签到,那么使用的内存就只是30w个bit,大约只占了940K,比最坏情况还要省一半的空间。
- 使用bitmap存储,最大的offset是2^32-1,也就是一个bitmap格式的key最大可以存储512M的数据。
- 使用bitmap存储的时候,有可能一开始是id较小的用户签到了,后面会有id较大的用户签到,这种情况下key的长度需要动态扩展,这需要花费一定的时间。在MBP2010上给offset为232-1分配512M的内存大约需要300ms,给offset为230-1分配128M的内存大约需要80ms,offset为228-1分配32M需要约30ms,offset为226-1分配8M大约需要8ms。当然,如果分配了可以容纳高位的空间后,使用低位时就不需要再扩容,比如一开始就通过setbit设置了第500w位的值,后面再使用offset小于500w的位都可以直接使用。
- 如果需要另外存储,可以每天用定时任务把数据写在需要的地方,比如MySQL。
四、适用场景
redis的bitmap操作虽然优点明显,但局限性也是显而易见的。因为它使用1bit来存储数据,所以只适用存储只有两个状态的数据,比如用户签到,资源(视频、文章、商品)的已读或未读状态。
关于redis的bitmap更多用法,可以参考官方文档。
用Redis实现签到功能的更多相关文章
- Redis位图实现用户签到功能
场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满 ...
- 基于Redis位图实现用户签到功能
场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满 ...
- Redis实战篇(二)基于Bitmap实现用户签到功能
很多应用上都有用户签到的功能,尤其是配合积分系统一起使用.现在有以下需求: 签到1天得1积分,连续签到2天得2积分,3天得3积分,3天以上均得3积分等. 如果连续签到中断,则重置计数,每月重置计数. ...
- 使用 UICollectionView 实现日历签到功能
概述 在 App 中,日历通常与签到功能结合使用.是提高用户活跃度的一种方式,同时,签到数据中蕴含了丰富的极其有价值的信息.下面我们就来看看如何在 App 中实现日历签到功能. 效果图 ..... 思 ...
- Redis多机功能介绍
Redis多机功能目的:以单台Redis服务器过渡到多台Redis服务器 Redis单机在生产环境中存在的问题 1.内存容量不足 Redis使用内存来存书数据库中的数据,但是对于一台机器来说,硬件的内 ...
- (三)开始在OJ上添加签到功能
在了解完OJ文件下的各个文件夹的主要作用后,我们开始往里面添加东西(其实只要知道各文件夹是干什么的后,添加东西也变得非常简单了) 一 在数据库中添加对应功能的字段. 我们这个学期才刚开数据库这门课,所 ...
- Redis的各项功能解决了哪些问题?
先看一下Redis是一个什么东西.官方简介解释到:Redis是一个基于BSD开源的项目,是一个把结构化的数据放在内存中的一个存储系统,你可以把它作为数据库,缓存和消息中间件来使用.同时支持string ...
- Redis的事务功能详解
Redis的事务功能详解 MULTI.EXEC.DISCARD和WATCH命令是Redis事务功能的基础.Redis事务允许在一次单独的步骤中执行一组命令,并且可以保证如下两个重要事项: >Re ...
- Redis实现排行榜功能(实战)
需求前段时间,做了一个世界杯竞猜积分排行榜.对世界杯64场球赛胜负平进行猜测,猜对+1分,错误+0分,一人一场只能猜一次.1.展示前一百名列表.2.展示个人排名(如:张三,您当前的排名106579). ...
随机推荐
- Windows 10 系统进入测试模式命令
首先需要开启测试模式,内容如下: @bcdedit -set loadoptions DDISABLE_INTEGRITY_CHECKS @bcdedit -set TESTSIGNING ON 取消 ...
- MinGW 可以编译驱动的
#include <ddk/ntddk.h> static VOID STDCALLmy_unload( IN PDRIVER_OBJECT DriverObject ) {} NTSTA ...
- Web中的相对路径和绝对路径
前台路径:浏览器端发起的资源请求路径 后台路径:服务器端发起的资源请求路径
- linux 发送邮件
参考资料:https://www.cnblogs.com/imweihao/p/7250500.html https://blog.csdn.net/liang19890820/article/det ...
- SparkSQL电商用户画像(三)之环境准备
五. 电商用户画像环境搭建 众所周知,Hive的执行任务是将hql语句转化为MapReduce来计算的,Hive的整体解决方案很不错,但是从查询提交到结果返回需要相当长的时间,查询耗时太长.这个主要原 ...
- QFNU 10-30 training
7-9 特立独行的幸福 题意:见PTA 思路:其实就是遍历进行查找,利用递归函数,为了解决是特立独行,还要用一个全局数组进行存储所有满足条件的数进行去重标记,最后在输出的时候进行判断是否是只读取过一次 ...
- 2020BUAA-个人博客-案例分析
个人博客作业-软件案例分析 项目 内容 北航2020软工 班级博客 作业要求 具体要求 我的课程目标 通过案例分析提升自己对于软件工程的认识 课程收获 分析软件,了解软件的定位 第一部分 调研,评测( ...
- Can’t update table ‘xxx’ in stored function/trigger because it is already used by statement which invoked this stored function/trigger
MySQL: Solution for ERROR 1442 (HY000): Can't update table 'xxx' in stored function/trigger because ...
- Date类常用方法总结(构造|格式化输出|String转换|Long转换|计算间隔|比较)
java.util.Date类 它重写了toString方法,new一个Date类直接输出是按照这样的格式 // "EEE MMM dd HH:mm:ss zzz yyyy"Fri ...
- vue常见错误
错误集锦 错误一 错误二 原因是写太多的import,修改呈如下方式 错误三 源码如下 原因是没有在return后面添加值 应该为 return false