当前位置: 首页 > java >正文

基于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.xdnf.cn/news/3341.html

相关文章:

  • C++——入门基础(2)
  • podman/docker国内可用的docker镜像源(2025-05)
  • 前端八股 3
  • Linux-04-搜索查找类命令
  • WPF实现数据库操作与日志记录
  • 工行手机银行安全吗?在应用商店下载工商银行安全吗?
  • 工 厂 模 式
  • 17. LangChain流式响应与实时交互:打造“类ChatGPT“体验
  • 数字智慧方案5974丨智慧农业大数据应用平台综合解决方案(79页PPT)(文末有下载方式)
  • 数据结构与算法学习笔记(Acwing提高课)----动态规划·背包模型(二)
  • 经典算法 青蛙跳杯子
  • 【大模型实战篇】华为信创环境采用vllm部署QwQ-32B模型
  • 【MySQL】复合查询与内外连接
  • 补题( Convolution, 二维卷积求输出矩阵元素和最大值)
  • 【方案分享】基于Three.js和Stencil Buffer的AR实物遮挡方案,支持不规则动态区域(AR地下设施、AR虚实遮挡)
  • 前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
  • ruoyi-plus Spring Boot + MyBatis 中 BaseEntity 的设计与动态查询实践
  • AVDictionary 再分析
  • 安全学习基础入门5集
  • curl详解
  • 综合案例建模(1)
  • 毕业论文 | 基于STM32的自动烟雾报警系统设计
  • 4.30阅读
  • Seata客户端@GlobalTransactional核心源码解析
  • Linux企业级分区设置
  • PEFT实战(三)——IA3参数高效微调
  • QT6 源(62)篇五:阅读与注释 QString 这个类,先给出官方综述,带一些翻译。总篇目太大,代码就有 2000 行
  • c++ 归并排序(分治)
  • 中国1km分辨率1901-2023年均气温降水数据
  • 2025年- H15-Lc123-41.缺失的第一个正数(普通数组)---java版