实战篇(一)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因其结构的特殊性,所以十分适合做用户签到的功能。

相关推荐
java1234_小锋2 小时前
Java高频面试题:讲一下 ZooKeeper 的持久化机制?
java·zookeeper·java-zookeeper
油墨香^_^2 小时前
Spring Cloud Feign 进阶详解:契约测试、负载均衡、文件上传与原生API
java·开发语言
闲人编程3 小时前
Redis分布式锁实现
redis·分布式·wpf·进程··死锁·readlock
前路不黑暗@3 小时前
Java项目:Java脚手架项目的地图服务(十)
java·数据库·spring boot·笔记·学习·spring cloud·maven
014-code4 小时前
Redisson 常用技巧
java·redis
java干货4 小时前
明明删了数据,磁盘却满了?
java
之歆4 小时前
HA 高可用集群指南
java·开发语言
tod1134 小时前
深入理解 Redis 事务:从原理到实践的完整解析
数据库·redis·缓存
CHANG_THE_WORLD4 小时前
指针入门一
java·前端·网络