Redis使用BitMap结构实现简单用户签到

redis引入场景:

       或许有人会说,我只需要使用mysql数据库就能完成用户签到记录的功能了呀,为什么还要使用 redis呢?
       用户签到一次,对应到mysql数据库中就是一条记录。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/821073e32b7840b2b3f73766f7c01952.png)        这里我们有6个字段,bigint的id、bigint的user_id、year类型的year、tiny类型的month、date类型的date、tinyint类型的is_backup。对应的字节数(8 + 8 + 1 + 1 + 3 + 1)=22字节,也就是用户签到一次就需要22字节来进行存储。我们假设一天只签到一次,按照30天来进行计算,22*30=660。也就是一个人每天签到一次,一个月需要660字节来进行存储,那当我们的用户量达到百万甚至千万,一年下来,需要的空间并不是我们可以接收的。

redis中的BitMap结构

我们按月来统计用户签到信息,签到记录为1,未签到则记录为0。把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。这样我们就用极小的空间,来实现了大量数据的表示。即便每天签到,一个月最多31位,也就是最多4个字节,与660字节相比,有百倍的容量差距。

BitMap的知识大致了解

  1. SETBIT:向指定位置(offset)存入一个0或1
  2. GETBIT :获取指定位置(offset)的bit值
  3. BITCOUNT :统计BitMap中值为1的bit位的数量
  4. BITFIELD :操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
实现思路

我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到。
注意:因为BitMap底层是基于String数据结构(这里的String是redis中的String),因此其操作也都封装在字符串相关的操作中了。

1. 定义接口

    /**
     * 我们在这里使用测试工具(例如postman)进行测试
     * 实现签到功能
     * @return 返回值
     */
    @PostMapping("/api/user/sign")
    public Result sign() {
    	// 具体实现方法我们放到Impl中进行实现,不在controller中进行过多的代码编写
        return userService.sign();
    }

2. 具体实现代码

    /**
     * 用户签到功能
     * @return 返回值
     */
    @Override
    public Result sign() {
        // 1. 获取当前登录用户id
        Long userId = UserHolder.getUser().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. 写入redis  
        /**
         * 第一个参数:key,这没什么好说的,因为redis就是 key - value进行存储的(但是这个key是与现实中的月相关联的,也就是不同的月份key是不同的)
         * 第二个参数: 这个位置是向哪个位置插入数据 这里减 1 的原因是因为redis是从0开始进行存储的,加入我们是第一天,对应到redis为下标为0
         * 第三个参数: 填写 true或者false,代表 1或者0
         */
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        return Result.ok();
    }

进行测试

  1. 发送请求(这里我进行了token验证,根据自己情况来)
    image
  2. 查看redis结果
    image
    因为我是在2022年11月15日进行测试,所以key是正确的,前面的1011是用户id,202211正确。下面为什么这么多0?因为redis不是根据位来进行存储的,而是字节存储形式, 一个字节8位,所以不满8的倍数的,自动补齐0。而1是第十五个位置,测试正确!!!

关于上面的一些用法说明

  • 为什么使用StringRedisTemplate而不是使用RedisTemplate。因为如果使用RedisTemplate的话,它会在我们存储对象信息的时候,会自动添加一行关于类的来源信息,因为RedisTemplate在对象与Json转换的时候不需要我们进行手动配置,但是它需要消耗我们的内存。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值