⛄导言

本文参阅黑马 点评项目

在各个项目中,咱们都可能需求用到报到和 核算功用。 报到后会给用户一些礼品以此来招引用户继续在该渠道进行活泼。

报到功用,咱们能够经过Redis中的 BitMap功用来完结

一、Redis BitMap 基本用法

⛅BitMap 基本语法、指令

报到功用咱们能够运用MySQL来完结,比方下表:

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

用户一次报到,便是一条记载,假如有1000万用户,均匀每人每年报到次数为10次,则这张表一年的数据量为 1亿条

每报到一次需求运用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则最多需求600多字节

这样的害处,占用内存太大了,极大的消耗内存空间!

咱们能够依据 Redis中 提供的 BitMap 位图功用来完结,每次报到与未报到用0 或1 来标识 ,一次存31个数字,只用了2字节 这样咱们就用极小的空间完结了报到功用

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 完结功用完结

服务器Redis版本选用 6.2

进入redis查询 SETBIT 指令

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

新增key 进行存储

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

查询 GETBIT指令

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

检查指定坐标的报到状态

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

查询 BITFIELD

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

无符号查询

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

BITPOS 查询1 和 0 第一次呈现的坐标

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

二、SpringBoot 整合 Redis 完结报到 功用

☁️需求介绍

选用BitMap完结报到功用

  • 完结报到接口,将当时用户当天报到信息保存到Redis中

思路剖析:

咱们能够把 年和月 作为BitMap的key,然后保存到一个BitMap中,每次报到就到对应的位上把数字从0 变为1,只要是1,就代表是这一天报到了,反之咋没有报到。

完结报到接口,将当时用户当天报到信息保存至Redis中

阐明
恳求方法 POST
恳求途径 /user/sign
恳求参数
回来值

提示: 由于BitMap 底层是基于String数据结构,因而其操作都封装在字符串操作中了。

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

⚡核心源码

UserController

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

UserServiceImpl

public Result sign() {
    //1. 获取登录用户
    Long userId = UserHolder.getUser().getId();
    //2. 获取日期
    LocalDateTime now = LocalDateTime.now();
    //3. 拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    //4. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    //5. 写入redis setbit key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth -1, true);
    return Result.ok();
}

接口进行测验

ApiFox进行测验

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

检查Redis 数据

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

三、SpringBoot 整合Redis 完结 报到核算功用

问题一: 什么叫做接连报到天数?

从最终一次报到开端向前核算,直到遇到第一次未报到停止,核算总的报到次数,便是接连报到天数。

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

逻辑剖析:

取得当时这个月的最终一次报到数据定义一个计数器,然后不断的向前核算,直到取得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完一切的数据,就能够取得当时月的报到总天数了

问题二: 如何得到本月到今天停止的一切报到数据?

BITFIELD key GET u[dayOfMonth] 0

假定今天是7号,那么咱们就能够从当时月的第一天开端,取得到当时这一天的位数,是7号,那么便是7位,去拿这段时间的数据,就能拿到一切的数据了,那么这7天里面报到了多少次呢?核算有多少个1即可。

**问题三:**如何从后向前遍历每个Bit位?

留意:bitMap回来的数据是10进制,哪假如说回来一个数字8,那么我哪儿知道究竟哪些是0,哪些是1呢?

咱们只需求让得到的10进制数字和1做与运算就能够了,由于1只要遇见1 才是1,其他数字都是0 ,咱们把报到成果和1进行与操作,每与一次,就把报到成果向右移动一位,依次内推,咱们就能完结逐一遍历的效果了。

需求:

完结以下接口,核算当时到当时时间在本月的接连天数

阐明
恳求方法 GET
恳求途径 /user/sign/count
恳求参数
回来值 接连报到的天数

有用户有时间咱们就能够组织出对应的key,此时就能找到这个用户截止这天的一切报到记载,再依据这套算法,就能核算出来他接连报到的次数了

核心源码

UserController

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

UserServiceImpl

public Result signCount() {
    //1. 获取登录用户
    Long userId = UserHolder.getUser().getId();
    //2. 获取日期
    LocalDateTime now = LocalDateTime.now();
    //3. 拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    //4. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    //5. 获取本月到今天停止的一切的报到记载,回来的是一个十进制的数字 BITFIELD sign:5:202301 GET u3 0
    List<Long> result = stringRedisTemplate.opsForValue().bitField(
        key,
        BitFieldSubCommands.create()
        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
    //没有任务报到成果
    if (result == null || result.isEmpty()) {
        return Result.ok(0);
    }
    Long num = result.get(0);
    if (num == null || num == 0) {
        return Result.ok(0);
    }
    //6. 循环遍历
    int count = 0;
    while (true) {
        //6.1 让这个数字与1 做与运算,得到数字的最终一个bit位 判别这个数字是否为0
        if ((num & 1) == 0) {
            //假如为0,报到完毕
            break;
        } else {
            count ++;
        }
        num >>>= 1;
    }
    return Result.ok(count);
}

进行测验

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

检查 Redis 变量

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

从今天开端,往前查询 接连报到的天数,成果为2 测验无误!

四、关于运用bitmap来处理缓存穿透的计划

回忆缓存穿透

发起了一个数据库不存在的,redis里面也不存在的数据,通常你能够把他当作一个进犯

处理计划:

  • 判别id<0

  • 数据库为空的话,向redis里面把这个空数据缓存起来

第一种处理计划:遇到的问题是假如用户拜访的是id不存在的数据,则此时就无法收效

第二种处理计划:遇到的问题是:假如是不同的id那就能够避免下次过来直击数据

所以咱们如何处理呢?

咱们能够将数据库的数据,所对应的id写入到一个list调集中,当用户过来拜访的时分,咱们直接去判别list中是否包含当时的要查询的数据,假如说用户要查询的id数据并不在list调集中,则直接回来,假如list中包含对应查询的id数据,则阐明不是一次缓存穿透数据,则直接放行。

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

现在的问题是这个主键其实并没有那么短,而是很长的一个 主键

哪怕你单独去提取这个主键,但是在 11年左右,淘宝的产品总量就已经超过10亿个

所以假如选用以上计划,这个list也会很大,所以咱们能够运用bitmap来削减list的存储空间

咱们能够把list数据抽象成一个非常大的bitmap,咱们不再运用list,而是将db中的id数据使用哈希思维,比方:

id 求余bitmap长度 :id % bitmap.size = 算出当时这个id对应应该落在bitmap的哪个索引上,然后将这个值从0变成1,然后当用户来查询数据时,此时已经没有了list,让用户用他查询的id去用相同的哈希算法, 算出来当时这个id应当落在bitmap的哪一位,然后判别这一位是0,仍是1,假如是0则标明这一位上的数据必定不存在,选用这种方法来处理,需求重点考虑一个事情,便是误差率,所谓的误差率便是指当发生哈希冲突的时分,产生的误差。

微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计

⛵小结

以上便是【Bug 终结者】对 微服务 Spring Boot 整合 Redis BitMap 完结 报到与核算 的简单介绍,报到功用是很常用的,在项目中,是一个不错的亮点,核算功用也是各大体系中比较重要的功用,报到完结后,去核算本月的接连 报到记载,来给予奖赏,可大大增加用户对体系的活泼度 技术改变世界!!!

假如这篇【文章】有协助到你,希望能够给【Bug 终结者】点个赞,创作不易,假如有对【后端技术】、【前端范畴】感兴趣的小可爱,也欢迎重视❤️❤️❤️ 【Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】!