基于Redis实现-用户签到

基于Redis实现-用户签到

这个功能将使用到Redis中的BitMap来实现。

我们按照月来统计用户签到信息,签到记录为1,未签到则记录为0

在这里插入图片描述

把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路称为位图(BitMap)。

Redis中是利用String类型数据结构实现BitMap,因此最大上限是512M,转化为bit则是2的32次方个bit位。相比于使用数据库字段来存储,内存使用大大减小。

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. SETBIT - 用户签到
# 用户ID 1001 在第5天签到(offset从0开始)
SETBIT user:sign:1001 4 1# 用户ID 1001 在第10天签到
SETBIT user:sign:1001 9 1
#-------------------------------------------------------------------------------------------
#2. GETBIT - 检查某天是否签到
# 检查用户ID 1001 第5天是否签到
GETBIT user:sign:1001 4
# 返回1表示已签到# 检查用户ID 1001 第6天是否签到
GETBIT user:sign:1001 5
# 返回0表示未签到
#-------------------------------------------------------------------------------------------
#3. BITCOUNT - 统计签到总天数
# 统计用户ID 1001 本月签到总天数
BITCOUNT user:sign:1001
# 返回签到的总天数
#-------------------------------------------------------------------------------------------
#4. BITFIELD - 批量操作签到数据
# 获取用户ID 1001 前5天的签到情况(以无符号5位整数形式返回)
BITFIELD user:sign:1001 GET u5 0# 同时设置多个签到日
BITFIELD user:sign:1001 SET u1 15 1 SET u1 16 1
#-------------------------------------------------------------------------------------------
#5. BITFIELD_RO - 只读方式获取签到数据
# 安全地获取用户ID 1001 前10天的签到情况
BITFIELD_RO user:sign:1001 GET u10 0
#-------------------------------------------------------------------------------------------
#6. BITOP - 多用户签到情况统计
# 创建两个用户的签到数据
SETBIT user:sign:1001 0 1
SETBIT user:sign:1001 1 1
SETBIT user:sign:1002 0 1# 统计哪些天数两个用户都签到了(按位与操作)
BITOP AND both_sign user:sign:1001 user:sign:1002# 查看结果
GETBIT both_sign 0  # 返回1,表示第1天都签到了
GETBIT both_sign 1  # 返回0,表示第2天不是都签到了
#-------------------------------------------------------------------------------------------
#7. BITPOS - 查找连续签到
# 查找用户ID 1001 第一次签到的位置(从第0位开始查找值为1的位)
BITPOS user:sign:1001 1# 查找用户ID 1001 第一次未签到的位置
BITPOS user:sign:1001 0
#-------------------------------------------------------------------------------------------
#完整签到系统示例
# 用户1001连续7天的签到情况(1已签,0未签)
SETBIT user:sign:1001 0 1  # 第1天
SETBIT user:sign:1001 1 1  # 第2天
SETBIT user:sign:1001 2 0  # 第3天未签
SETBIT user:sign:1001 3 1  # 第4天
SETBIT user:sign:1001 4 1  # 第5天
SETBIT user:sign:1001 5 0  # 第6天未签
SETBIT user:sign:1001 6 1  # 第7天# 查询签到情况
BITCOUNT user:sign:1001  # 返回5(共签到了5天)
BITPOS user:sign:1001 0  # 返回2(第一个未签到的位置)

2.使用Java实现简单的用户签到

注意:因为BitMap底层是基于String数据结构,因此其操作都封装在字符串操作中。

在这里插入图片描述

1.实现用户签到

//在ServiceImpl@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sign() {//1.获取当前用户Long UserId = UserHolder.getUser().getId();//2.获取当前日期LocalDateTime now = LocalDateTime.now();//3.拼接keyString 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 Result.ok();}

2.Java实现统计连续签到

1.什么是;连续签到天数?

从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续的签到天数。

2.如何得到本月到今天为止的所有签到数据?

BITFIELD key GET u[dayOfMonth] 0

3.如何从后向前遍历每个bit位?

与1做与运算,就能得到最后一个bit位。

随后右移一位,下一个bit为就成为了最后一个bit位。

//在ServiceImpl@Autowiredprivate StringRedisTemplate stringRedisTemplate;
@Override
public Result signCount() {// 1.获取当前登录用户IDLong UserId = UserHolder.getUser().getId();// 2.获取当前日期时间(带时区)LocalDateTime now = LocalDateTime.now();// 3.拼接Redis键:sign:用户ID:年月(例如:sign:1001:202310)String KeySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));String key = "sign:" + UserId + ":" + KeySuffix;// 4.获取今天是本月的第几天(1-31)int dayOfMonth = now.getDayOfMonth();// 5.使用BITFIELD命令获取本月签到数据的位图(返回无符号整数)// 格式:BITFIELD key GET u<dayOfMonth> 0// 表示从偏移量0开始,获取dayOfMonth长度的无符号整数List<Long> longs = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));// 5.1 处理空结果情况if (longs == null || longs.isEmpty()) {return Result.ok(0);  // 无签到记录返回0}// 5.2 获取位图转换后的十进制数值Long num = longs.get(0);if (num == null || num == 0) {return Result.ok(0);  // 数值为0表示无签到}// 6.通过位运算计算连续签到天数int count = 0;while (true) {// 6.1 检查最低位是否为1(与1做按位与运算)// 结果为0表示未签到,1表示已签到if ((num & 1) == 0) {break;  // 遇到未签到日终止循环} else {count++;  // 签到日计数器+1}// 6.2 无符号右移一位(相当于删除已检查的最低位)// 例如:1011(11) >>> 1 = 0101(5)num >>>= 1;}// 7.返回连续签到天数return Result.ok(count);
}

关于BITFIELD参数使用解释

  1. key

    • 作用:Redis 中存储 BitMap 的键名

    • 示例"sign:1001:202310"(用户1001在2023年10月的签到数据)

    • 底层命令BITFIELD key [GET type offset]

    • 说明:指定要操作的 BitMap 键

  2. BitFieldSubCommands.create()

    • 作用:创建 BITFIELD 命令的子命令构建器

    • 说明

      • Spring Data Redis 的封装方法,用于构建复杂的 BITFIELD 操作
      • 对应 Redis 原生命令中的 [GET/SET/INCR ...] 部分
  3. get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))

  • 作用:指定要获取的位段类型和长度

  • 参数分解

    • BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)
      • unsigned:表示获取无符号整数(值始终 ≥0)
      • dayOfMonth:整数位数长度(例如今天是10月25日,则 dayOfMonth=25
      • 底层逻辑:Redis 会将从偏移量0开始的25个bit转换为一个无符号整数
  • 示例
    如果签到数据为 1011...(二进制),unsigned(25) 会将其转换为十进制整数(如 123456)。

  1. valueAt(0)

    • 作用:指定要获取的起始位偏移量(offset)

    • 参数

      • 0:表示从 BitMap 的第0位开始获取
    • 关键点

      • 偏移量从0开始计数(与 SETBIT/GETBIT 的偏移量规则一致)
      • 配合 unsigned(dayOfMonth) 表示:从第0位开始,获取连续 dayOfMonth 个bit
  2. valueAt(0)

    • 作用:指定要获取的起始位偏移量(offset)

    • 参数

      • 0:表示从 BitMap 的第0位开始获取
    • 关键点

      • 偏移量从0开始计数(与 SETBIT/GETBIT 的偏移量规则一致)
      • 配合 unsigned(dayOfMonth) 表示:从第0位开始,获取连续 dayOfMonth 个bit

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

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

相关文章

如何用GPU Instancing来优化树木草石重复模型

1&#xff09;如何用GPU Instancing来优化树木草石重复模型 2&#xff09;Unity ASTC压缩后的纹理在部分安卓机型上不显示 3&#xff09;现在大部分项目的竖版UI设计分辨率是多少 4&#xff09;Android上拖拽物体不实时跟随手指的问题 这是第430篇UWA技术知识分享的推送&#x…

Java面试高频问题(31-33)

三十一、服务网格&#xff1a;东西向流量治理与故障注入 服务网格架构分层 mermaid graph BT subgraph Control Plane APilot --> BEnvoy Sidecar CMixer --> B DCitadel --> B end subgraph Data Plane B --> E服务A B --> F服务B B --> G服务C end 核心能…

初学python的我开始Leetcode题8-3

提示&#xff1a;100道LeetCode热题-8-3主要是二叉树相关&#xff0c;包括三题&#xff1a;将有序数组转换为二叉搜索树、验证二叉搜索树、二叉搜索树中第K小的元素。由于初学&#xff0c;所以我的代码部分仅供参考。 目录 前言 题目1&#xff1a;将有序数组转换为二叉搜索树…

1996-2022年全国31省ZF干预度数据/财政干预度数据(含原始数据+计算过程+结果)

1996-2022年全国31省ZF干预度数据/财政干预度数据&#xff08;含原始数据计算过程结果&#xff09; 1、时间&#xff1a;1996-2022年 2、来源&#xff1a;国家统计局和各省年鉴 3、指标&#xff1a;地方财政一般预算支出、地区生产总值&#xff08;GDP&#xff09;、ZF干预度…

g4f升级到0.5.2.0版本了,但是有些机器无法运行,只能降级到0.5.1.2版本

g4f升级到0.5.2.0版本了&#xff0c;跟0.5.1.2更以前的版本相比&#xff0c;主要更新为增加了可以设置Huggingface等供应商的key Providers API key HuggingFace:Get API key HuggingSpace: 因为很多模型都会调用Huggingface&#xff0c;所以最好设置Huggingface的API key。…

C语言教程(二十五):C 语言函数可变参数详解

引言: 在 C 语言编程中,有时我们需要处理参数数量不固定的情况,比如常见的 printf 函数,它可以根据格式化字符串的要求接受任意数量的参数。这种能接受不确定数量参数的函数,就是可变参数函数。下面将深入探讨其定义、实现原理、使用方式、示例以及注意事项。 一、可变参…

OpenStack Yoga版安装笔记(25)Nova Cell理解

1、Nova Cell概述 &#xff08;官方文档&#xff1a;Cells (v2) — nova 25.2.2.dev5 documentation&#xff09; Nova中的cells功能的目的是允许较大的部署将其多个计算节点分割成多个cell。所有的nova部署都默认是cell部署&#xff0c;即使大多数情况下只有单一cell。这意味…

Java Set<String>:如何高效判断是否包含指定字符串?

在 Java 开发中&#xff0c;我们经常使用 Set 集合来存储一组唯一性的元素。特别是 HashSet&#xff0c;由于其基于哈希表的实现&#xff0c;在进行元素查找&#xff08;判断是否包含&#xff09;时通常具有非常高的效率&#xff08;平均时间复杂度 O(1)&#xff09;。 那么&a…

MySQL 查找指定表名的表的主键

原理 SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME 表名 AND CONSTRAINT_NAME PRIMARY方法 public static String getPk(String tableName) {String sql "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TA…

Java大厂面试突击:从Spring Boot自动配置到Kafka分区策略实战解析

第一轮核心知识 面试官:请解释Spring Boot中自动配置的工作原理并演示如何自定义一个@ConfigurationProperties组件? xbhog:自动配置通过EnableAutoConfiguration注解触发,结合当前环境判断(如是否检测到MyBatis依赖)和条件注解(@ConditionalOnClass)来决定是否启用配…

开发板型号 ESP32-DevKitC-32模块型号 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3

以下是关于开发板型号 ESP32-DevKitC-32、模块型号 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3 的详细介绍&#xff1a; 开发板型号&#xff1a;ESP32-DevKitC-32 概述&#xff1a;ESP32-DevKitC 是乐鑫推出的一款基于 ESP32 模组的小型开发板&#xff0c;板上模组的绝大部…

数据库系统综合应用与深度实践指南

前言 在当今数据驱动的时代&#xff0c;数据库技术已成为信息系统的核心支柱。从简单的数据存储到复杂的企业级应用&#xff0c;数据库系统支撑着现代社会的方方面面。本文作为一篇综合性的数据库科普文章&#xff0c;旨在为读者提供从基础到进阶的完整知识体系&#xff0c;涵…

vscode 的空格和 tab 设置 与 Rime 自建词库

自动保存&#xff08;多用于失去焦点时保存&#xff09; Files: Auto Save 推荐不勾 保存时格式化&#xff08;Pritter 插件的功能&#xff0c;自动使用 Pritter 的格式&#xff09; Editor: Format On Save 推荐不勾 tab 的空格数量&#xff0c;2 或 4 Editor: Tab Size 推荐…

【Python爬虫详解】第五篇:使用正则表达式提取网页数据

在前面几篇文章中&#xff0c;我们介绍了几种强大的HTML解析工具&#xff1a;BeautifulSoup、XPath和PyQuery。这些工具都是基于HTML结构来提取数据的。然而&#xff0c;有时我们需要处理的文本可能没有良好的结构&#xff0c;或者我们只关心特定格式的字符串&#xff0c;这时正…

论文报错3

idm不让用&#xff1a; powershell管理员运行&#xff1a; irm https://raw.githubusercontent.com/lstprjct/IDM-Activation-Script/main/IAS.ps1 | iex 选择1&#xff1a; 输入9&#xff1a;

数据结构-树(二叉树、红黑、B、B+等)

​树的基本定义​ 树的定义 树&#xff08;Tree&#xff09;​​ 是一种 ​​非线性数据结构​​&#xff0c;由 ​​节点&#xff08;Node&#xff09;​​ 和 ​​边&#xff08;Edge&#xff09;​​ 组成&#xff0c;满足以下条件&#xff1a; ​​有且仅有一个根节点&am…

【Android】四大组件

目录 1. Activity 2. Service 3. BroadcastReceiver 4. ContentProvider 四大组件各自承担着不同的职责&#xff0c;彼此之间协同工作&#xff0c;共同为用户提供一个流畅的APP体验。 1. Activity 负责展示用户界面&#xff0c;就像App的一个个“页面”&#xff0c;用户通…

Java 多线程进阶:线程安全、synchronized、死锁、wait/notify 全解析(含代码示例)

在 Java 并发编程中&#xff0c;“线程安全” 是核心议题之一。本文将深入讲解线程安全的实现手段、synchronized 的使用方式、可重入锁、死锁的成因与避免、wait/notify 通信机制等&#xff0c;并配合实际代码案例&#xff0c;帮助你彻底搞懂 Java 线程协作机制。 一、线程安全…

高并发场景下的MySQL生存指南

引言 在2025年全球数字经济峰会上&#xff0c;阿里云披露其核心交易系统单日处理请求量突破万亿次&#xff0c;其中MySQL集群承载了78%的OLTP业务。这标志着数据库系统已进入百万级QPS时代&#xff0c;传统优化手段面临三大挑战&#xff1a; 一、硬件与架构优化&#xff1a;构…

MCP入门

什么是mcp mcp&#xff08;model context protocol&#xff0c;模型上下文协议&#xff09; 标准化协议&#xff1a;让大模型用统一的方式来调用工具&#xff0c;是llm和工具之间的桥梁 A2A&#xff1a;Agent-to-Agent协议 mcp通信机制 提供mcp服务查询的平台 具有工具合集…