⛄导言
本文参阅黑马 点评项目
在各个项目中,咱们都可能需求用到报到和 核算功用。 报到后会给用户一些礼品以此来招引用户继续在该渠道进行活泼。
报到功用,咱们能够经过Redis中的 BitMap功用来完结
一、Redis BitMap 基本用法
⛅BitMap 基本语法、指令
报到功用咱们能够运用MySQL来完结,比方下表:
用户一次报到,便是一条记载,假如有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 指令
新增key 进行存储
查询 GETBIT指令
检查指定坐标的报到状态
查询 BITFIELD
无符号查询
BITPOS 查询1 和 0 第一次呈现的坐标
二、SpringBoot 整合 Redis 完结报到 功用
☁️需求介绍
选用BitMap完结报到功用
- 完结报到接口,将当时用户当天报到信息保存到Redis中
思路剖析:
咱们能够把 年和月 作为BitMap的key,然后保存到一个BitMap中,每次报到就到对应的位上把数字从0 变为1,只要是1,就代表是这一天报到了,反之咋没有报到。
完结报到接口,将当时用户当天报到信息保存至Redis中
阐明 | |
---|---|
恳求方法 | POST |
恳求途径 | /user/sign |
恳求参数 | 无 |
回来值 | 无 |
提示: 由于BitMap 底层是基于String数据结构,因而其操作都封装在字符串操作中了。
⚡核心源码
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进行测验
检查Redis 数据
三、SpringBoot 整合Redis 完结 报到核算功用
问题一: 什么叫做接连报到天数?
从最终一次报到开端向前核算,直到遇到第一次未报到停止,核算总的报到次数,便是接连报到天数。
逻辑剖析:
取得当时这个月的最终一次报到数据,定义一个计数器,然后不断的向前核算,直到取得第一个非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);
}
进行测验
检查 Redis 变量
从今天开端,往前查询 接连报到的天数,成果为2 测验无误!
四、关于运用bitmap来处理缓存穿透的计划
回忆缓存穿透:
发起了一个数据库不存在的,redis里面也不存在的数据,通常你能够把他当作一个进犯
处理计划:
-
判别id<0
-
数据库为空的话,向redis里面把这个空数据缓存起来
第一种处理计划:遇到的问题是假如用户拜访的是id不存在的数据,则此时就无法收效
第二种处理计划:遇到的问题是:假如是不同的id那就能够避免下次过来直击数据
所以咱们如何处理呢?
咱们能够将数据库的数据,所对应的id写入到一个list调集中,当用户过来拜访的时分,咱们直接去判别list中是否包含当时的要查询的数据,假如说用户要查询的id数据并不在list调集中,则直接回来,假如list中包含对应查询的id数据,则阐明不是一次缓存穿透数据,则直接放行。
现在的问题是这个主键其实并没有那么短,而是很长的一个 主键
哪怕你单独去提取这个主键,但是在 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则标明这一位上的数据必定不存在,选用这种方法来处理,需求重点考虑一个事情,便是误差率,所谓的误差率便是指当发生哈希冲突的时分,产生的误差。
⛵小结
以上便是【Bug 终结者】对 微服务 Spring Boot 整合 Redis BitMap 完结 报到与核算 的简单介绍,报到功用是很常用的,在项目中,是一个不错的亮点,核算功用也是各大体系中比较重要的功用,报到完结后,去核算本月的接连 报到记载,来给予奖赏,可大大增加用户对体系的活泼度 技术改变世界!!!
假如这篇【文章】有协助到你,希望能够给【Bug 终结者】点个赞,创作不易,假如有对【后端技术】、【前端范畴】感兴趣的小可爱,也欢迎重视❤️❤️❤️ 【Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】!