用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). ...
随机推荐
- POJ3114强连通+spfa
题意: 给你n个点,m条有向边,q询问,每次询问给两个数a,b输出a->b的最短路,但是题目有个限制,就是在一个环上的任意两点距离为0. 思路: 简单题目,直接强连通压缩 ...
- Linux提权
讲Linux提权之前,我们先看看与Linux有关的一些知识: 我们常说的Linux系统,指的是Linux内核与各种常用软件的集合产品,全球大约有数百款的Linux系统版本,每个系统版本都有自己的特性和 ...
- Intel汇编语言程序设计学习-第三章 汇编语言基础-下
3.4 定义数据 3.4.1 内部数据类型 MASM定义了多种内部数据类型,每种数据类型都描述了该模型的变量和表达式的取值集合.数据类型的基本特征是以数据位的数目量的大小:8,16,32,,48, ...
- 码农飞升记-03-OpenJDK是什么?
目录 1.OpenJDK 概述 2.OpenJDK 的发展史 3.OpenJDK Community 1.角色定义 Participant(参与者) Contributor(贡献者) OpenJDK ...
- jpa查找数据库最新一条消息
主要字段说明: pid:指导记录主键 user_pid:用户主键 competition_project_pid:用户作品 Mysql表 Repository /** * 指导记录 * @date 2 ...
- MindSpore模型验证
技术背景 在前面一篇博客中,我们介绍了MindSpore在机器学习过程中保存和加载模型的方法.这种将模型存储为静态文件的做法,使得我们可以更灵活的使用训练出来的模型,比如用于各种数据集的验证,或者是迁 ...
- Morgan Stanley Books List:经典金融书籍推荐
一.经济学 1. 中华帝国的专制制度,佛朗索瓦.魁奈 2. 资本论(共3卷),马恩全集 3. 国家竞争优势,麦克尔.波特 4. Essentials of corporate analysis, by ...
- [bug] @Test注解无法使用
参考 https://blog.csdn.net/lixiangxiang666/article/details/83745901
- 什么是环境变量,Linux环境变量及作用 echo
什么是环境变量,Linux环境变量及作用 < Linux命令的执行过程是怎样的?(新手必读)Linux PATH环境变量是什么,有什么用?(入门必读) > <Linux就该这么学&g ...
- /etc/ssh/sshd_config ssh自动断 cent7
vim /etc/ssh/sshd_config ClientAliveInterval 60ClientAliveCountMax 8630000 ClientAliveInterval 30Cli ...