springboot + redis实现签到与统计功能
在很多项目中都会有签到与统计功能,最容易想到的方案是创建一个签到表来记录每个用户的签到记录,比如设计一个mysql数据库表:
CREATE TABLE tb_sign
id bigint(20) unsigned NOT NULL AUTOINCREMENT COMMENT '主键',
user_id bigint(20) unsigned NOT NULL COMMENT '用户ID',
sign_date date NOT NULL COMMENT '签到的日期',
is_backup tinyint(1) unsigned DEFAUL TNULL COMMENT '是否补签',
PRIMARY KEY (id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW FORMAT=COMPACT;
用户签到一次就可以往表里添加一条记录;但是这样有一个坏处,就是占用的内存太大了,会极大的消耗内存空间;比如有1万用户,每个用户每个月签到10次,那么一个月就是10万条记录,一年就是120万条;如果用户更多并且签到的次数越多,那么数据量就会更大哦。
签到一次需要使用8+8+3+1 = 20个字节,如果使用redis中的bitmap来实现,每次签到与未签到用1与0来表示,那么只需要2个字节即可了,这样极大的节约了内存;那么接下来认识与使用bitmap。
1.bitmap基本操作指令
SETBIT:向指定位置(offset)存入一个0或1
GETBIT:获取指定位置(offset)的bit值
BITCOUNT:统计BitMap中值为1的bit位的数量
BITFIELD:操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
BITFIELD_RO:获取BitMap中bit数组,并以十进制形式返回
BITOP:将多个BitMap的结果做位运算(与 、或、异或)
BITPOS:查找bit数组中指定范围内第一个0或1出现的位置
1.1 新增


1.2 查询


1.3 统计值为1的数量


1.4 查询1 和 0 第一次出现的坐标


2.springboot整合redis
-
创建一个spring boot项目,这里比较简单,不用过多介绍;
-
添加redis依赖
<!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--spring2.x集成redis所需common-pool2--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.11.0</version></dependency>
- 配置配置文件
#redis服务器地址
spring.redis.host=127.0.0.1
#redis服务器连接端口
spring.redis.port=6379
#redis数据库索引(默认是0)
spring.redis.database=0
#连接超时时间
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中最小空闲连接
spring.redis.lettuce.pool.min-idle=0
- 测试一下
@SpringBootTest
class SpringbootRedisSigninApplicationTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testvoid testRedisSet() {stringRedisTemplate.opsForValue().set("name", "picacho");String name = (String)stringRedisTemplate.opsForValue().get("name");System.out.println(name);}}

在redis中也可以看到我们插入进去的数据;

3. 实现
我们可以用年和月作为BitMap的key,然后保存到一个BitMap中,每次签到就把对应的位上把数字从0变为1,如果是1,就表示这一天签到了,反之就表示没有签到。
3.1 实现签到的核心代码
这里主要讨论基本思路和处理流程,因此代码并没有非常规范,仅仅作为示例看待即可;
- UserController
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/sign")public String sign(){return userService.sign();}
}
- UserService
@Service
public class UserService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;public String sign(){// 1.模拟获取用户idLong userId = 1L;// 2.获取日期LocalDateTime now = LocalDateTime.now();// 3.拼接key字符串String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.写入redisstringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return "ok";}
}
- 测试一下

- 查看redis,可以看到本月26号完成了签到。

3.2 统计连续签到次数的核心代码
这里先构造几天签到的测试数据便于测试使用;我们这里构造了26,25,24,22号完成了签到。

我们需要获取本月到今天为止的所有签到数据,今天是26号,那么我们就可以从当前月的第一天开始,获得到26号的位数,那么就是26位,去拿这段时间的数据,就能拿到所有的数据了,那么这26天里边签到了多少次呢?统计有多少个1即可。
注意:bitMap返回的数据是10进制,哪假如说返回一个数字8,我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次,就把签到结果向右移动一位,依次类推即可。
- UserController
@PostMapping("/count")public String countSign(){return userService.countSign();}
- UserService
public Integer countSign(){// 1.模拟获取用户idLong userId = 1L;// 2.获取日期LocalDateTime now = LocalDateTime.now();// 3.拼接key字符串String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.统计签到次数List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));// 5.1 没有签到结果if(result == null || result.isEmpty()){return 0;}Long num = result.get(0);if(num == null || num == 0){return 0;}// 5.2 统计签到次数int count = 0;while(true){if((num & 1) == 0){break;}else{count++;}num >>>= 1;}return count;}
- 测试一下

可以看到24,25,26号完成了连续3天的的签到,刚好是3天。
到这里demo就结束了,源码地址:demo地址
相关文章:
springboot + redis实现签到与统计功能
在很多项目中都会有签到与统计功能,最容易想到的方案是创建一个签到表来记录每个用户的签到记录,比如设计一个mysql数据库表: CREATE TABLE tb_sign id bigint(20) unsigned NOT NULL AUTOINCREMENT COMMENT 主键, user_id bigint(20) unsig…...
Redis | 数据结构(02)SDS
一、键值对数据库是怎么实现的? 在开始讲数据结构之前,先给介绍下 Redis 是怎样实现键值对(key-value)数据库的。 Redis 的键值对中的 key 就是字符串对象,而 value 可以是字符串对象,也可以是集合数据类型…...
Linux C语言开发-D7D8运算符
算术运算符:-*/%,浮点数可以参与除法运算,但不能参与取余运算 a%b:表示取模或取余 关系运算符:<,>,>,<,,! 逻辑运算符:!,&&,|| &&,||逻辑运算符是从左到右,依次运算&#…...
redis 配置主从复制,哨兵模式案例
哨兵(Sentinel)模式 1 . 什么是哨兵模式? 反客为主的自动版,能够自动监控master是否发生故障,如果故障了会根据投票数从slave中挑选一个 作为master,其他的slave会自动转向同步新的master,实现故障自动转义 2 . 原理…...
Python---练习:使用for循环实现用户名+密码认证
案例: 用for循环实现用户登录 ① 输入用户名和密码 ② 判断用户名和密码是否正确(usernamelaowang,passwordlw123) ③ 登录仅有三次机会,超过3次会报错 思考: 用户登陆情况有3种: ① 用户名错误(此时…...
react中使用jquery 语法
react中使用jquery 语法 npm install jquery引入 import $ from ‘jquery’ import React from react; import ./css/App.css import { Button } from antd; import $ from jquerylet slider_img [https://cdn.jsdelivr.net/gh/xaoxuu/cdn-wallpaper/abstract/41F215B9-261F…...
服务器中了360后缀勒索病毒怎么解决,勒索病毒解密,数据恢复
近期,网络上的各种病毒都比较猖獗,而其中较为明显的就是360后缀勒索病毒,从这个月开始云天数据恢复中心接到很多企业的求助,企业的服务器遭到了360后缀勒索病毒的攻击,通过给用户的服务器检测与加密病毒的分析…...
使用字节流读取文件中的数据的几种方式
public class FileReader02_ {public static void main(String[] args) {}Testpublic void m1() {String filePath "e:\\hello.txt";FileReader fileReader null;int date0;try {fileReader new FileReader(filePath);//循环读取 使用readwhile ((datefileReader.…...
Android WMS——概述(一)
Android 中的 WMS 指的是 Window Manager Service(窗口管理服务)。WMS 是 Android 系统中的核心服务,主要分为四大部分,分别是窗口管理,窗口动画,输入系统中转站和 Surface 管理 。负责管理应用程序窗口的创建、移动、调整大小和显示等操作。 一、功能简介 WMS 的职责可…...
Node编写获取用户信息接口
目录 前言 初始化路由模块 使用postman发送get获取用户信息请求 初始化路由处理函数模块 获取用户基本信息 前言 在前两篇文章中已经介绍了如何编写用户注册接口以及用户登录接口,这篇文章介绍如何获取用户信息,本篇文章建立在Node编写用户登录接口…...
【从0到1设计一个网关】自研网关的设计要点以及架构设计
文章目录 请求的流程架构设计设计要点项目架构流程设计源码地址: 源码地址 请求的流程 一个HTTP请求发送到网关并完成整个生命周期通常包括以下步骤: 客户端请求: 请求始于客户端,客户端通过HTTP请求(例如GET、POST等)发送请求到API网关的入口点。 API网关接收: API…...
论文-分布式-分布式计算|容错-分布式控制下的自稳定系统
参考文献Self-stabilizing systems in spite of distributed control可以把松散耦合的 循环序列过程 间的同步任务,看成是要保持一个这样的不变性:“系统要处于一种合法状态”因此每个进程在运行每一个可能会改变不变性的步骤之前都要先检查一下是可以执…...
C#压缩图片的方法
/// <summary> /// 图片压缩 /// </summary> /// <param name"imagePath">图片文件路径</param> /// <param name"targetFolder">保存文件夹</param> /// <param name"quality">压缩质量</param&g…...
安装 fcitx + 搜狗/谷歌输入法 之后导致 死机,重启后黑屏只有鼠标可以移动
一般的原因就是 : fcitx 导致的问题 方法就是 先卸载搜狗,再卸载fcitx 解决办法: 首先:ctrlaltF6 进入命令行界面,如果进不去就 ctrlaltF2 接下来执行: sudo apt-get remove sogoupinyin sudo apt-get …...
Maven项目转为SpringBoot项目
Maven项目转为SpringBoot项目 前言创建一个maven项目前的软件的一些通用设置Maven仓库的设置其他的设置字符编码编译器注解支持 创建的Maven项目修改为Spring Boot项目修改pom.xml文件修改启动类-Main新建WAR包所需的类 添加核心配置文件 测试的控制器最后整个项目的目录结构![…...
C语言之预处理
目录 前言 宏定义define的用法 文件包含include的用法 条件编译的用法 其他预处理命令 练习题 练习一 练习二 练习三 前言 预处理命令可以改变程序设计环境,提高编程效率,它们并不是C语言本身的组成部分,不能直接对它们进行编译&am…...
css步骤条
html 代码以及样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>css步骤条</title><style>.steps {display: flex;justify-content: space-between;padding: 0;margin: 20px 10px;lis…...
[Hive] 常见函数
文章目录 字符串函数数值函数随机函数日期和时间函数字符串转时间 聚合函数数组函数结构体函数数组函数映射函数 map正则处理JSON 字符串函数 CONCAT(string1, string2, …):将多个字符串连接成一个字符串。 LENGTH(string):返回字符串的长度。 LOWER…...
Mac用NTFS文件夹读写NTFS硬盘 NTFS能复制多大的文件
Mac作为一款备受欢迎的计算机操作系统,具备了许多令人惊叹的功能和特性。然而,对于一些Mac用户来说,使用NTFS格式的硬盘可能存在一些疑问。他们可能想知道Mac是否能够读写NTFS格式的硬盘,以及NTFS格式的硬盘是否有文件大小的限制。…...
【unity3D】Scroll Rect组件—制作下滑列表
💗 未来的游戏开发程序媛,现在的努力学习菜鸡 💦本专栏是我关于游戏开发的学习笔记 🈶本篇是unity的Scroll Rect组件 Scroll Rect组件 基础知识详细说明案例演示——制作一个简单的下滑框扩展 介绍:Scroll Rect组件是用…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
