既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
new Random().nextInt(7) + 29, TimeUnit.MINUTES);
![在这里插入图片描述](https://img-blog.csdnimg.cn/14faa7c702824233a26e0b550d6b9d9e.png)
### 缓存击穿:热点key
>
> 让key永不过期
>
>
>
某个key访问量突然暴增,微博某个词条访问量瞬间很大,一直转圈
此时,数据库一直查询某个key,一直查询某个值;
![在这里插入图片描述](https://img-blog.csdnimg.cn/95c9df874b45442d83e880b57f00c7a7.png)
>
> 解决方案:让key 永不过期
>
>
>
### 缓存穿透【重要】
指客户端请求的数据在缓存和数据库中都没有,这样缓存永远不会生效,这些请求都会打到数据库;
如果有人查询100w条数据都打到数据库,数据库就会瘫痪;
![在这里插入图片描述](https://img-blog.csdnimg.cn/1a90528d3c434768aab6318a548327f0.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/727212ab197547f8ac2c12b9d7fcfbff.png)
## 穿透的解决方案:布隆过滤器
![在这里插入图片描述](https://img-blog.csdnimg.cn/03e5b48e3e9c4a10bdd18d97115866d8.png)
布隆过滤器,就是一种数据结构,它是由一个长度为m bit的位数组与n个hash函数组成的数据结构,位数组中每个元素的初始值都是0。在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1。这样,就相当于把所有的key保存到了布隆过滤器中了。
>
> 结论:如果布隆过滤器中没有,则一定没有,常用作白名单;
>
>
>
即:布隆过滤器返回值:
如果布隆过滤器返回没有,则结论正确,阻止访问;
如果布隆过滤器返回有,则结论不一定正确;
项目应用:
如果白名单中没有,则对方恶意访问缓存,阻止访问;
### 问题:如何存储100w纯数字
在连续的空间存储100w个纯数字占用多少内存
package com.tianju.redisDemo.testDemo;
public class BigMapDemo {
public static void main(String[] args) {
int[] a = {1,2,3,4,5,6,7,8,9};
int[] b = new int[1000000]; // 存100w个数字
// 占用空间,1个int占用32 bits空间
System.out.println(32*1000000.0/1024/1024+“MB”);
}
}
int— 32bits
100w x 32bits= 32,000,000(bits)
1kb = 1024 bits;
1M = 1024kb
结论:100w个纯数字占用了30MB的内存;
解决办法,用BigMap,1int,对应32个bits,可以用来表示0~31个数字,如下图所示,可以表示数字1,5;大大节省了空间
![在这里插入图片描述](https://img-blog.csdnimg.cn/878ed2ee199d4491bb9ea64ace346900.png)
### 布隆过滤器
布隆过滤器(Bloom Filter)本质上是由长度为 m 的位向量或位列表(仅包含 0 或 1 位值的列表)组成,最初所有的值均设置为 0,如下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/78fc292c67614ec0ad98be928e30df8c.png)
为了将数据项添加到布隆过滤器中,会提供 K 个不同的哈希函数,并将结果位置上对应位的值置为 “1”。
![在这里插入图片描述](https://img-blog.csdnimg.cn/22c6a0b9e09a484f8fcbe15c5c65ea05.png)
如上图所示,当输入 “semlinker” 时,预设的 3 个哈希函数将输出 2、4、6,我们把相应位置 1。假设另一个输入 ”kakuqo“,哈希函数输出 3、4 和 7。你可能已经注意到,索引位 4 已经被先前的 “semlinker” 标记了。此时,我们已经使用 “semlinker” 和 ”kakuqo“ 两个输入值,填充了位向量。当前位向量的标记状态为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/2ab9b42a563940009668fa0b3c12edfd.png)
当对值进行搜索时,与哈希表类似,我们将使用 3 个哈希函数对 ”搜索的值“ 进行哈希运算,并查看其生成的索引值。假设,当我们搜索 ”fullstack“ 时,3 个哈希函数输出的 3 个索引值分别是 2、3 和 7:
![在这里插入图片描述](https://img-blog.csdnimg.cn/1239255f8908477b9fd8da682819a500.png)
从上图可以看出,相应的索引位都被置为 1,这意味着我们可以说 ”fullstack“ 可能已经插入到集合中。事实上这是误报的情形,产生的原因是由于哈希碰撞导致的巧合而将不同的元素存储在相同的比特位上。幸运的是,布隆过滤器有一个可预测的误判率(FPP):
![在这里插入图片描述](https://img-blog.csdnimg.cn/61ed508f20684f518d8dd4274eda4a8b.png)
* n 是已经添加元素的数量;
* k 哈希的次数;
* m 布隆过滤器的长度(如比特数组的大小);
极端情况下,当布隆过滤器没有空闲空间时(满),每一次查询都会返回 true 。这也就意味着 m 的选择取决于期望预计添加元素的数量 n ,并且 m 需要远远大于 n 。
实际情况中,布隆过滤器的长度 m 可以根据给定的误判率(FFP)的和期望添加的元素个数 n 的通过如下公式计算:
![在这里插入图片描述](https://img-blog.csdnimg.cn/96629a9ed3e945c7bbb2d6942c6d5c32.png)
了解完上述的内容之后,我们可以得出一个结论,
**当我们搜索一个值的时候,**
**若该值经过 K 个哈希函数运算后的任何一个索引位为 ”0“,那么该值肯定不在集合中。**
**但如果所有哈希索引值均为 ”1“,则只能说该搜索的值可能存在集合中**。
### 项目应用:布隆过滤器≈白名单
![在这里插入图片描述](https://img-blog.csdnimg.cn/66c4b3fa369d4765b81f5372c6648f2e.png)
如果布隆过滤器中没有,则一定没有,可以证明没有;
>
> 结论:
> 如果布隆过滤器返回没有,这个结论一定正确;
> 如果布隆过滤器返回有,这个结论不一定正确;
>
>
>
如果白名单中没有,则对方恶意访问缓存,则不通过,阻止访问;
#### htool工具包案例
<https://www.bookstack.cn/read/hutool-5.6.0-zh>
![在这里插入图片描述](https://img-blog.csdnimg.cn/db13f054a60047f388656288505b0965.png)
// 初始化
BitMapBloomFilter filter = new BitMapBloomFilter(10);
filter.add("123");
filter.add("abc");
filter.add("ddd");
// 查找
filter.contains("abc")
## Redis项目应用(六)布隆过滤器—白名单
### 业务逻辑
后台查询用户,防止穿透缓存到数据库查询用户,导致数据库压力大;
* 预热的时候,用户信息加载到布隆过滤器中;
* 查询业务,比如查询用户时,先用布隆过滤器过滤,再去redis;
![在这里插入图片描述](https://img-blog.csdnimg.cn/86ab5c98d7f14e21a97a250acadb6e4a.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/66c4b3fa369d4765b81f5372c6648f2e.png)
>
> 以一个根据用户名查询用户是否在数据库中案例为例;
>
>
>
1.先到布隆过滤器中,在controller层解决,拦阻非法请求;
2.通过布隆过滤器,在redis缓存中查找,查询成功返回;
3.通过布隆过滤器,在redis缓存中没找到,查询数据库,查询成功返回,并且更新缓存
### 布隆过滤器工具类 BitMapBloomFilter
WhiteListBloomFilter.java工具类
package com.tianju.redisDemo.util;
import cn.hutool.bloomfilter.BitMapBloomFilter;
/**
* 白名单的布隆过滤器
*/
public class WhiteListBloomFilter {
// Params: m – M值决定BitMap的大小
private static BitMapBloomFilter bloomFilter = new BitMapBloomFilter(10);
// TODO:布隆过滤器有,缓存没有,需要从数据库查询
/\*\*
* 将str加入布隆过滤器中,作为白名单使用
* @param str
*/
public static void addBloom(String str){
bloomFilter.add(str);
}
/\*\*
* 判断str是否在布隆过滤器中
* @param str
* @return true表示可能在布隆过滤器中;false表示一定不在布隆过滤器中
*/
public static Boolean isInBloom(String str){
return bloomFilter.contains(str);
}
}
### 缓存预热+布隆过滤器初始化@Scheduled(cron = “0 01 18 \* \* ?”)
UsernamesPreHot.java缓存预热
package com.tianju.redisDemo.job;
import com.tianju.redisDemo.dao.UserMapper;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.util.WhiteListBloomFilter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.Schedules;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* 采用 @Scheduled(cron = “0 54 21 * * ?”)进行预热
*/
@Slf4j
@Component
public class UsernamesPreHot {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private UserMapper userMapper;
@Scheduled(cron = "0 01 18 \* \* ?")
public void preHot() {
// 1.清除缓存中的数据
stringRedisTemplate.delete("usernames");
// 2.更新缓存中的数据
// 3.也要加入到布隆过滤器中
List<User> userList = userMapper.selectList(null);
// TODO:布隆过滤器有,缓存没有,需要从数据库查询
WhiteListBloomFilter.addBloom("Arya");
userList.forEach(user ->{
stringRedisTemplate.opsForSet().add("usernames", user.getUsername());
// 放到布隆过滤器中
WhiteListBloomFilter.addBloom(user.getUsername());
}
);
log.debug("redis缓存预热 + 布隆过滤器预热,usernames");
}
}
### service层:布隆–>缓存–>数据库
package com.tianju.redisDemo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianju.redisDemo.dao.UserMapper;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
Transactional;
import java.util.List;
[外链图片转存中…(img-OTi9QpVC-1715005654478)]
[外链图片转存中…(img-VQL8TnPA-1715005654478)]
[外链图片转存中…(img-URaCE6gZ-1715005654479)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新