首页 > 数据库 >利用Redis的BitMap统计每月用户连续签到

利用Redis的BitMap统计每月用户连续签到

时间:2024-10-13 13:20:43浏览次数:3  
标签:签到 Redis BitMap 获取 Result bit now

利用Redis的BitMap统计每月用户连续签到

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

把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。这样我们就用极小的空间,来实现了大量数据的表示

Redis中是利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是 2^32个bit位。

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出现的位置
实现签到功能

​ 我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到。

UserController

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

UserServiceImpl

@Override
public Result sign() {
    // 1. 获取当前登录用户的ID
    // UserHolder 是一个工具类,用于获取当前登录用户的信息
    Long userId = UserHolder.getUser().getId();
    
    // 2. 获取当前的日期和时间
    // LocalDateTime.now() 获取当前的时间点
    LocalDateTime now = LocalDateTime.now();
    
    // 3. 拼接Redis中的键名
    // 使用当前时间按照“:yyyyMM”的格式格式化,作为键名的一部分
    // USER_SIGN_KEY 是预定义的键名前缀,用于标识签到记录
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix;
    
    // 4. 获取今天是本月的第几天
    // getDayOfMonth() 方法返回当前日期在这个月中的天数
    int dayOfMonth = now.getDayOfMonth();
    
    // 5. 写入Redis,设置签到记录
    // 使用SETBIT命令在Redis中设置指定位置的bit位为1
    // 这里的偏移量是从0开始的,因此需要将dayOfMonth减去1
    // 设置为true表示用户已经签到
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    
    // 6. 返回操作成功的结果
    // Result.ok() 表示操作成功,通常会返回HTTP 200状态码
    return Result.ok();
}
连续签到统计

UserController

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

UserServiceImpl

@Override
public Result signCount() {
    // 1. 获取当前登录用户的ID
    Long userId = UserHolder.getUser().getId();
    
    // 2. 获取当前的时间
    LocalDateTime now = LocalDateTime.now();
    
    // 3. 根据当前时间和用户ID拼接Redis中的键名
    // 这里使用了格式化字符串的方式,将当前时间按“:yyyyMM”的格式转换成字符串,作为键名的一部分
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix; // USER_SIGN_KEY 是定义好的前缀
    
    // 4. 获取当前日期是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    
    // 5. 从Redis中获取该用户本月截至今天的签到记录
    // 使用BITFIELD命令来获取特定位置上的bit值。这里的操作是获取从0开始到dayOfMonth-1位的无符号整数值
    List<Long> result = stringRedisTemplate.opsForValue().bitField(
            key, 
            BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
    );
    
    // 6. 如果没有找到任何签到记录,返回0表示没有连续签到
    if (result == null || result.isEmpty()) {
        return Result.ok(0);
    }
    
    // 7. 获取查询的结果,如果结果为空或为0,也表示没有连续签到
    Long num = result.get(0);
    if (num == null || num == 0) {
        return Result.ok(0);
    }
    
    // 8. 初始化连续签到天数计数器
    int count = 0;
    
    // 9. 循环检查每个位上的值,以确定连续签到的天数
    while (true) {
        // 9.1. 对数字进行与运算,检查最低位(即最右边的位)是否为1
        // 这里使用了位运算符&,与1进行与运算可以检查最低位是否为1
        if ((num & 1) == 0) {
            // 9.2. 如果最低位为0,则表示这一天没有签到,结束循环
            break;
        } else {
            // 9.3. 如果最低位为1,则表示这一天有签到,计数器加1
            count++;
        }
        
        // 9.4. 将数字无符号右移一位,准备检查下一位
        // 使用无符号右移运算符>>>=,保证即使最高位为1,右移后也不会变成负数
        num >>>= 1;
    }
    
    // 10. 返回连续签到的天数
    return Result.ok(count);
}

完成

标签:签到,Redis,BitMap,获取,Result,bit,now
From: https://blog.csdn.net/2301_81717523/article/details/142886952

相关文章

  • Redisson分布式锁
    目录一、SETNX+EXPIRERedis分布式锁误删问题业务阻塞造成锁超时释放问题二、SETNX+LUA脚本Lua脚本解决多条命令原子性问题利用Java代码调用Lua脚本改造分布式锁 锁无法续期问题三、Redisson分布式锁 背景和定义使用方式Redisson的大致流程Redisson的可重入原理......
  • Redis教程
    (原创)Redis教程......
  • 基于nodejs+vue基于Redis的网约车系统[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着城市化进程的加速和共享经济的蓬勃发展,网约车系统已成为现代城市交通的重要组成部分。它不仅为乘客提供了便捷、灵活的出行方式,也为司机提供了增加收入......
  • 高级java每日一道面试题-2024年10月13日-数据库篇[Redis篇]-怎么保证缓存和数据库数据
    如果有遗漏,评论区告诉我进行补充面试官:怎么保证缓存和数据库数据的一致性?我回答:在分布式系统中,保证缓存和数据库数据的一致性是一个常见的挑战。由于缓存的引入主要是为了提高系统的性能和响应速度,但这也带来了数据一致性的问题。以下是一些常用的方法来保证缓存和......
  • 高级java每日一道面试题-2024年10月11日-数据库篇[Redis篇]-Redis都有哪些使用场景?
    如果有遗漏,评论区告诉我进行补充面试官:Redis都有哪些使用场景?我回答:Redis是一个开源的、基于键值对的数据结构存储系统,,它支持多种数据类型,包括字符串、散列、列表、集合和有序集合。它可以用作数据库、缓存和消息中间件。由于其高性能、丰富的数据结构支持以及多种......
  • 用C/C++构建自己的Redis——第六章、事件循环和计时器
    用C/C++构建自己的Redis——第六章、事件循环和计时器文章目录用C/C++构建自己的Redis——第六章、事件循环和计时器前言一、超时和计时器二、链表三、事件循环四、链表排序4.1寻找最近的计时器4.2激活计时器4.3维护计时器五、测试总结前言这一章我们将一起学......
  • 【Golang】使用gob格式存储数据到redis
    目录1、背景2、gob和json对比3、go库下载4、代码示例【1】redis初始化【2】封装gob编码和解码方法【3】定义gob编码和解码的数据结构【4】gob编码【5】gob解码5、总结1、背景之前在压测大数据量的业务场景时,通过pprof分析cpu耗时,发现主要耗时在json序列化和反序列化......
  • python redis使用教程
    文章目录安装Redispython安装redis库使用Python连接Redis使用Redis实现缓存Redis中的常用缓存操作Redis缓存策略发布与订阅事务安装RedisRedisWindows最新安装教程(2024.10.10)python安装redis库pipinstallredisE:\Redis-x64-3.2.......
  • Redis 单线程模型
    Redis是单线程的,但仍然非常快,主要得益于以下几个因素:I/O多路复用:Redis使用I/O多路复用技术(比如epoll),使它能够高效处理大量连接,即便是单线程内存操作:Redis的大部分操作都是内存级别的,避免了磁盘I/O的瓶颈避免上下文切换:由于是单线程,Redis不需要频繁地在线程之间切换,......
  • Redis可视化工具Redis Desktop Manager(附安装包)
    前言redis工具,我相信每个开发都需要,如果每次查都去client执行指令,我怕查完之后,老大就要发版咯。我之前一直用的Redis可视化工具RedisDesktopManager,总觉得差点意思,直到同事推荐了个新的,突然2眼发光!!先上链接:Redis可视化工具(含我之前用的简约版,跟同事发我的新版)RedisDes......