实战篇(一)BitMap实现签到功能

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


项目背景

现要求实现一个签到功能,要求如下:

  • 能够判断用户是否已经进行签到
  • 能够判断用户连续签到的天数

1、理论讲解

Bitmap是一个存储二进制数字的数组,数组中每个元素的下标被称为offset。

Bitmap 不是Redis中的实际数据类型,而是在String类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全的块,且最大长度为512MB,它们适合用于设置最多 232个不同的位。

命令 介绍
SETBIT key offset value 设置指定 offset 位置的值
GETBIT key offset 获取指定 offset 位置的值
BITCOUNT key start end 获取 start 和 end 之间值为 1 的元素个数
BITOP operation destkey key1 key2 ... 对一个或多个 Bitmap 进行运算,可用运算符有 AND, OR, XOR 以及 NOT

2、实现方式

用户签到功能如下所示:

  • key的设计:"sign:" + userId + 日期(格式为yyyyMM)
  • 写入Redis中(比如当月第5天,就将Bitmap对应的第4个位置设置为1)
java 复制代码
    @Override
    public void sign() {
        // 1.获取当前登录用户
        Long userId = AuthContextUtil.getUserInfo().getId();
        System.out.println(userId);
        // 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. 写入Redis SETBIT key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    }

统计今天截止的连续签到次数:

  • key的设计:"sign:" + userId + 日期(格式为yyyyMM)相同业务下格式保持一致
  • 获取本月为止的所有签到记录,返回的是一个十进制的数字
  • 该数字的二进制形式的最低位代表的是今天签到的情况,最高位代表着是当月第一天的签到情况。
  • 获取当月至今为止的签到情况,只需要从低位到高位以此与1做位运算即可,直到为0,即为断签。
java 复制代码
public int signCount() {
        // 1.获取当前登录用户
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 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)
        );
        if(result == null || result.isEmpty()){
            // 没有任何签到结果
            return 0;
        }

        Long num = result.get(0);
        if(num == null || num == 0){
            return 0;
        }
        int count = 0;
        // 6. 循环遍历
        // 如果为0,说明未签到,结束
        // 如果不为0,说明已签到,计数器+1
        while ((num & 1) != 0) {
            // 6.1. 让这个数字与1做与运算,得到数字的最后一个bit位,判断这个bit位是否为0
            count++;
            num >>>= 1;
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
        }
    return count;
    }

总结

BitMap因其结构的特殊性,所以十分适合做用户签到的功能。

相关推荐
leikooo4 小时前
用餐厅和盘子理解栈和堆
java·数据结构·链表·rust
Flynt5 小时前
Redis Cluster主节点挂了,为什么"高可用"还全员掉线?我把三次kill的记录翻出来了
数据库·redis·分布式
Ivanqhz5 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
沙漠之主6 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
FPGA小徐6 小时前
PA1 基础设施(2)深度解析:NEMU 中 ITRACE、MTRACE、FTRACE、IRINGBUF、ETRACE、DIFFTEST 全解析
java·服务器·网络
YOLO数据集集合6 小时前
EvoAgent:面向PR研发治理的自进化Multi-Agent Harness系统
java·开发语言·目标检测·agent·自进化
知行EDI7 小时前
知行之桥 MaBang 端口使用指南——Create Order 订单创建篇
java·服务器·数据库
笑鸿的学习笔记7 小时前
C++笔记之大块顺序写
java·c++·笔记
打工仔折腾 AI7 小时前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战
写后端的胖头鱼7 小时前
一文详解Cache Aside(旁路缓存模式)
redis·缓存·延迟双删·缓存一致