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

Redis BitMap 实现签到及连续签到统计

一、引言

        用户签到功能是很多应用都离不开的一个板块,单词打开、QQ达人等等为我们所熟知,这项功能该如何实现呢,一些朋友可能想当然的觉得无非将每日的签到数据记录下来不就好了,不会去细想用谁记录,如何记录才合适。

        假如我们计入传统的关系型数据库,以MySQl为例,我们分别用INT、TINYINT、DATE分别存储用户ID、是否签到(0或1)、当天日期,那么每条记录将占用8字节(4+1+3),当用户达到一定规模,每月的签到数据存储将占用很大空间,统计查找效率也低下。

        为了解决这一问题,我们引入今天要介绍的一种Redis中的数据结构BitMap(位图)。

二、简介及基本操作

1.简介

Redis 的 Bitmap(位图)是一种基于位操作的数据结构,底层实际上是字符串(String)类型,但可以将字符串视为一个由二进制位组成的数组。每个位只能是 0 或 1,因此 Bitmap 非常适合用于存储和处理大量的布尔状态信息,而且非常节省空间。

2.基本操作

  • SETBIT:设置指定偏移量(offset)上的位的值(0 或 1)。

  • GETBIT:获取指定偏移量上的位的值。

  • BITCOUNT:计算指定范围内值为 1 的位的数量。

  • BITOP:对多个 Bitmap 进行位运算(AND、OR、XOR、NOT),并将结果存储到新的 Bitmap 中。

  • BITPOS:查找指定范围内第一个值为 0 或 1 的位的位置。

 关于位图的详细命令及RedisTemplate的详细内容大家可以自行了解。

三、签到实现

整个操作还是比较简单的,我们在收到签到请求后获取到用户信息,时间数据拼接为key对用户当月的签到操作做记录,即在位图对应位数上做写1操作(controller比较简单大家自己随手写一个测试即可)

    @Override
    public void sign() {
        //获取用户
        Long uid = UserHolder.getUser().getId();
        //获取日期
        LocalDateTime now = LocalDateTime.now();
        //拼接key
        String keySuffix= now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key=RedisConstants.USER_SIGN_KEY+uid+keySuffix;
        //本月第几天
        int dayOfMonth = now.getDayOfMonth();
        //写入redis,位图是从0开始索引的,所以减一
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
    }

 接下来我们用PostMan 做一下签到测试

可以看到Redis中已经存储了当前用户三月份的签到数据,也就是今天(03-31)的签到,BitMap第31位为1,代表03-31以签到,并且这一月的数据仅占4B的空间。

四、连续签到统计实现

        这里的连续签到即指当月中从当前天起往回计数,直到未签到的日子的总数,即今天没签那就算断了(如果要统计当月签到总数的话自然可以用bitCount直接统计)。

        那怎么对BitMap进行这种倒叙的计数统计呢,其实我们从其二进制的存储结构就能看出端倪,我们直接用BitMap数据和1进行与运算判断当前的最后一位是否为1,条件满足则计数并且无符号右移一位,继续对当前最后一位做判断直到不满足条件。

    public Integer signCount() {
        //获取用户
        Long uid = UserHolder.getUser().getId();
        //获取日期
        LocalDateTime now = LocalDateTime.now();
        //拼接key
        String keySuffix= now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key=RedisConstants.USER_SIGN_KEY+uid+keySuffix;
        //本月第几天
        int dayOfMonth = now.getDayOfMonth();
        //截至今天的位图签到数据
        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;
        //循环遍历
        while (true){
            //和1做与运算,得到最后一个比特位,再和0比较
            if ((num & 1 ) == 0) {
                //为零,未签到
                break;
            }else {
                //为1继续计数
                count++;
            }
            //无符号右移1位,切换下一比特位
            num>>>=1;
        }
        return count;
    }

 我们改动一下刚刚的BitMap数据,为了方便我就不用BitField命令了,直接用工具改成如下数据

然后再用PostMan做测试,得到的连续签到天数也是4天

        本次分享主要为大家介绍一下BitMap在这种签到业务中的应用,比较简单,到这里已经全部结束,感谢大家阅读。

相关文章:

  • Dart之库和可见性和异步支持、生成器、可调用类与Isolates、Typedefs和元数据
  • 微前端 - 以无界为例
  • C语言库zlog日志库工具
  • 23种设计模式-结构型模式-组合
  • RabbitMQ消息队列面试题集合
  • 如何使用 FastAPI 构建 MCP 服务器
  • 【电动汽车再生制动控制技术(万字长文,图文并茂)】
  • 全国职业院校技能大赛 网络建设与运维样题解析
  • 11-SpringBoot3入门-整合aop
  • 分布式计算Ray框架面试题及参考答案
  • 喜讯 | 耘瞳科技视觉检测与测量装备荣膺“2024机器视觉创新产品TOP10”
  • SerDes(Serializer/Deserializer)详解
  • SOME/IP-SD -- 协议英文原文讲解10
  • 广度优先搜索(BFS)与深度优先搜索(DFS)解析
  • 通义万相2.1 你的视频创作之路
  • Web-ssrfme:redis 未授权访问攻击
  • 【go】数组与切片
  • GoLand 2024.3 中文 GO语言开发工具
  • 什么是架构,以及当前市面主流架构类型有哪些?
  • 智能车载终端测试:慧通测控多参数综合测试定制化方案
  • 营销型网站建设培训/大数据获客系统
  • 如何快速自己做网站/东莞网络优化公司
  • ubuntu wordpress端口/优化营商环境心得体会个人
  • 榆林公司网站建设/哈尔滨seo关键词优化
  • 连接品硕网线做怎么弹网站/全网引流推广
  • 网站运营策略/外链网盘源码