用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). ...
随机推荐
- xxl-job滥用netty导致的问题和解决方案
netty作为一种高性能的网络编程框架,在很多开源项目中大放异彩,十分亮眼,但是在有些项目中却被滥用,导致使用者使用起来非常的难受. 本篇文章将会讲解xxl-job作为一款分布式任务调度系统是如何滥用 ...
- Nginx配置动静分离
简单解释 所谓动静分离指的是当访问静态资源时,路由到一台静态资源服务器,当访问是非静态资源时,路由到另外一台服务器 配置实现 修改server块 server块配置参考,配置规则可自行修改,符合正则语 ...
- 如何在spring boot中从控制器返回一个html页面?
项目截图 解决方法 我之前用的@RestController注解,而@RestController这个控制器返回数据而不是视图,改成@Controller 就好了(以下是修改后的) @Controll ...
- h5实现电子签名
前端需要引入:jSignature.min.js ,jquery-1.9.1.min.js前端 部分写法:body内加 <div id="signature">< ...
- File & Directory
新开一节IO(Input/Output)的用法. 这节主要讲一下操作文件和文件目录的两个静态类:File 和 Directory. 在进入正题之前,先理解一下相对路径和绝对路径这两个概念: 绝对路径, ...
- SQLFlow使用中的注意事项--设置篇
SQLFlow 是用于追溯数据血缘关系的工具,它自诞生以来以帮助成千上万的工程师即用户解决了困扰许久的数据血缘梳理工作. 数据库中视图(View)的数据来自表(Table)或其他视图,视图中字段(Co ...
- NumPy之:ndarray多维数组操作
NumPy之:ndarray多维数组操作 目录 简介 创建ndarray ndarray的属性 ndarray中元素的类型转换 ndarray的数学运算 index和切片 基本使用 index wit ...
- sscanf的应用
1.提取字符串 2.提取指定长度的字符串 3.提取指定字符为止的字符串 4.取仅包含指定字符集的字符串 5.取到指定字符集为止的字符串 #include <stdio.h> int mai ...
- JAVA基础——变量、常量
变量 java中,变量时最基本的存储单元,其要素包括变量名,变量类型和作用域. 注意事项: 每个变量都有类型,类型可以是基本类型,也可以是引用类型. 变量名必须是合法的标识符 变量声明时一条完整的语句 ...
- zabbix监控之邮件报警通知
zabbix官网的操作指南:https://www.zabbix.com/documentation/4.0/zh/manual 首先我们需要创建一个需要被监控的主机,并设置相应的监控项.当监控项收集 ...