Redis:原理速成+项目实战——Redis实战14(BitMap实现用户签到功能)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:Redis:原理速成+项目实战——Redis实战13(GEO实现附近商铺、滚动分页查询)
📚订阅专栏:Redis:原理速成+项目实战
希望文章对你们有所帮助

用户签到是各个APP一个重要的功能,可以促进用户对APP的使用,所以用户签到,以及连续签到天数的统计都是很重要的。
这里将会基于BitMap(位图)来实现用户签到,而Redis刚好支持BitMap的数据结构。

BitMap实现用户签到

  • BitMap功能
  • 实现签到功能
  • 统计连续签到

BitMap功能

用一张表来存储用户签到信息:
在这里插入图片描述
多个用户、连续多天,会有很多的签到数据,这样的设计会占用太大的内存,对数据库的压力也很大。
解决方法:用位图(BitMap)来实现,最大用31bit即可统计一个用户一个月的签到记录。按月来统计用户签到信息,签到记录为1,否则为0。
Redis中利用String类型的数据结构实现BitMap,因此最大上限是512M,转换为bit则是2^32bit,32个bit位完全足够完成一个月的签到。
BitMap操作命令:

SETBIT:向指定位置(offset)存入一个0或1
GETBIT:获取指定位置(offset)的bit值
BITCOUNT:统计BitMap中值为1的bit位的数量
BITFIELD:操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值,大多数时候用来做查询,修改操作用SETBIT即可,查询可以一次查多个位置的。
BITFIELD_RO:获取BitMap中bit数组,并以十进制形式返回
BITOP:将多个BitMap的结果做位运算(与、或、异或)
BITPOS:查找bit数组中指定范围内第一个0或1出现的位置

例如:

SETBIT bm1 0 1 # 给bm1的第0个位置设为1,表示第1天签到了(没有签到的话直接不用写语句,默认为0)
GETBIT bm1 2 # 查询第3天有没有签到
BITCOUNT # 查询共签到了几天
BITFIELD bm1 GET u2 0 # 从第一天开始算,获取2个bit位,并以无符号十进制整数返回
BITPOS bm1 0 # 第一个0出现的位置(第一次断签)

实现签到功能

发起Post请求,实现签到接口,将当前用户当天的签到信息保存到Redis中(key要包含用户以及日期信息(只需要年月))。而用户、当天时间,这些东西根本不需要前端来传,所以这是没有参数的接口。
因为BitMap底层是基于String的,因此相关操作也被封装到字符串相关操作中。

UserController:

	@PostMapping("/sign")public Result sign(){return userService.sign();}

UserServiceImpl:

    @Overridepublic Result sign() {//获取当前登录用户Long userId = UserHolder.getUser().getId();//获取当前日期LocalDateTime now = LocalDateTime.now();//将用户与日期拼接成key,日期只需要年月的字符串格式String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//"sign:"//获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//写入Redis SETBIT key offset 1stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();}

打开postman来测试接口:
在这里插入图片描述
查看Redis,今天1月13日,所以在坐标12标识为1,并且后面补0,补到字节整数倍(2字节16位)
在这里插入图片描述

统计连续签到

需求:统计本月到今天为止的连续签到次数,今天要是没签到直接就为0。

从最后一次签到开始向前统计,直到第一次未签到为止,总的签到次数即为连续签到天数。
剩下就是一个非常简单的模拟:

1、获取本月为止所有签到数据(BITFIELD key GET u[dayOfMonth] 0)
2、用1来做与运算,得到最后一个bit位
3、右移,即可获得下一个bit位

UserController:

	@GetMapping("sign/count")public Result signCount(){return userService.signCount();}

UserServiceImpl:

	@Overridepublic Result signCount() {//获取当前登录用户Long userId = UserHolder.getUser().getId();//获取当前日期LocalDateTime now = LocalDateTime.now();//将用户与日期拼接成key,日期只需要年月的字符串格式String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//"sign:"//获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//获取本月截止今天位置的所有签到记录,返回十进制数字List<Long> result = stringRedisTemplate.opsForValue().bitField(key,//设置为无符号数,且从第0位开始算,抽取出到今天为止的bit位BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if(result == null || result.isEmpty()){return Result.ok(0);}//与1做与运算,得到数字的最后一个bit位Long num = result.get(0);if(num == 0 || num == null){//为0,未签到,结束return Result.ok(0);}//为1,循环遍历,为1则计数器+1int count = 0;while (true){if((num & 1) == 1){count++;num >>>= 1;//无符号右移1位}else{break;}}return Result.ok(count);}

打开postman做测试:
在这里插入图片描述
正式完成,这一部分的内容还是很基础的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/346189.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机组成原理】指令流水线的三种冒险情况(Hazards)

冒险 在计算机架构中&#xff0c;流水线冒险是指在指令流水线的执行过程中由于数据相关性或控制相关性而导致的一种性能问题。指令流水线是将指令执行过程划分为多个阶段&#xff0c;这样可以同时处理多条指令&#xff0c;从而提高指令执行的效率。然而&#xff0c;流水线执行…

进程的创建与回收学习笔记

目录 一、进程内容&#xff1a; 二、进程常用命令 三、创建子进程 四、子进程进阶 五、进程的退出 六、进程的回收 一、进程内容&#xff1a; 程序&#xff1a; 存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09; 静态的 进程&#xff1a; 执行一个程序所…

增广路算法 DFS求解 最大网络流问题

最大网络流问题 最大网络流问题是这样的&#xff0c;有一个有向图&#xff0c;假定有一个源点&#xff0c;有一个汇点&#xff0c;源点有流量出来&#xff0c;汇点有流量进入&#xff0c;有向图上的边的权重为该条边可通过的最大流量(方向为边的方向)&#xff0c;问从源点到汇…

Linux系统SSH远程管理服务

目录 一、SSH协议是什么&#xff1f; 1、SSH协议的定义&#xff1a; 2、SSH协议的优点 3、SSH的客户端与服务端 4、SSH的原理 4.1公钥首次连接原理 4.2ssh远程登录 4.3使用简单的SSH远程登录 二、OpenSSH服务器 1、OpenSSH简介 2、配置Openssh服务端 3、SSH服务的最…

每日一题——LeetCode1128.等价多米诺骨牌对的数量

先尝试暴力解法&#xff1a; var numEquivDominoPairs function(dominoes) {var count0for(let i0;i<dominoes.length-1;i){for(let ji1;j<dominoes.length;j){if((dominoes[i][0]dominoes[j][0] && dominoes[i][1]dominoes[j][1]) || (dominoes[i][0]dominoes…

数据库系统概念 第七版 中文答案 第2章 关系模型介绍

第2章 关系模型介绍 2.1 考虑图 2.17 中的员工数据库。这些关系上适当的主码是什么&#xff1f; Answer: 相应的主键如下所示: 2.2 考虑从 instructor 的 dept_name 属性到 department 关系的外键约束。请给出对这些关系的插入和删除的示例&#xff0c;使得它们破坏该外码约…

Java接入Apache Spark(入门环境搭建、常见问题)

Java接入Apache Spark&#xff08;环境搭建、常见问题&#xff09; 背景介绍 Apache Spark 是一个快速的&#xff0c;通用的集群计算系统。它对 Java&#xff0c;Scala&#xff0c;Python 和 R 提供了的高层 API&#xff0c;并有一个经优化的支持通用执行图计算的引擎。它还支…

Docker容器进入的4种方式(推荐最后一种)

在使用Docker创建了容器之后&#xff0c;大家比较关心的就是如何进入该容器了&#xff0c;其实进入Docker容器有好几多种方式&#xff0c;这里我们就讲一下常用的几种进入Docker容器的方法。 进入Docker容器比较常见的几种做法如下&#xff1a; 使用docker attach使用SSH使用…

RK3568驱动指南|第十二篇 GPIO子系统-第134章 三级节点操作函数实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

CSS 下载进度条

<template><view class=btn>下载中</view></template><script></script><style>/* 设置整个页面的样式 */body {width: 100vw; /* 页面宽度为视口宽度 */background: #000000; /* 背景颜色为白色 */display: flex; /* 使用 flex…

python|切片

切片的语法格式 object[start_index:end_index:step] &#xff08;1&#xff09;step步长&#xff1a;有正负&#xff0c;正表示正着走&#xff0c;负表示负着走&#xff0c;步长的绝对值代表一步走的距离。 &#xff08;其中&#xff0c;在深度学习中卷积也有步长的说法&…

【博士每天一篇论文-理论分析】Dynamical systems, attractors, and neural circuits

阅读时间&#xff1a;2023-11-19 1 介绍 年份&#xff1a;2016 作者&#xff1a;Paul Miller 马萨诸塞州沃尔瑟姆市布兰代斯大学Volen国家复杂系统中心 期刊&#xff1a; F1000Research 引用量&#xff1a;63 这篇论文主要关注神经回路中的动力系统和吸引子。作者指出神经回路…