一、缓存穿透
1. 简介
用户查询数据,如果缓存中没有数据,就会去数据库中查询,然后再加入缓存中,之后的请求就可以在缓存中查询了;
如果用户在查询的时候,使用非法请求,每次查询到的都是数据库中没有的数据,这样就无法存入缓存之中了;
这样就会导致,如果用户连续发送这种redis和数据库都不存在值的查询,并且请求成千上万次,由于在缓存中查询不到而直接请求数据库,给数据库带来压力,造成宕机,从而影响整个系统,就被称为缓存穿透。
2. 解决方案
- 对于数据库中不存在的数据,,也要在缓存中存入一个空数据:比如空字符串,空对象,空数组或list,对于空数据的缓存,最好设置一个过期时间(因为该数据可能现在为空,但是过段时间就不为空了);
- 使用布隆过滤器
3.布隆过滤器
它就相当于在Redis前加了一层过滤器,用户请求去查询数据,如果缓存中和数据库中都没有数据,那么布隆过滤器中也必定没有,这样请求经过过滤器得知数据库中没有数据,那么请求就不会访问缓存和数据库了;
3.1一些缺点:
-
如果删除了数据库和redis中的数据,但是布隆过滤器中的数据是没法删除的,因为一个二进制单位中可能绑定有多个key,没法达到修改二进制单位为0而达到删除效果;
-
布隆过滤器存在误判的概念,不过二进制数组越大,误差率越低;
-
使用布隆过滤器,代码复杂度会增大, 维护难度会增大,(维护一个集合,这个集合中存在很多的key)
-
集群和分布式环境下,布隆过滤器需要和redis结合使用,数据需要保存到redis中,保证各个节点都要可用
3.2 基本原理
根据存储对象的key值计算出来的hash,找出存放的位置,例如 subCat:1 这个Redis的key值,经过多个hash函数计算,存放到上图红色虚线所指的位置,当他再被请求到的时候,该key计算出的hash值如果也指向的 二进制 数都是 1 的时候;那么大致就可以认为,这个key值的对象是在数据库或者缓存中有值的;
3.3 出现误判的场景:
可能这个key在数据库或者缓存中其实不存在,但是计算出来这个key的hash值,所指向的二进制数的位置,是已经被其他key所指向的位置,且全部都刚好为1;那么这个key也会被认为在数据库和缓存中存在, 这种情况就是误判的情况(见上图蓝色虚线)。
如果布隆过滤器判断某个key不存在,那这个key一定不存在;
如果布隆过滤器判断某个key存在,那这个key不一定存在;
3.4 为什么布隆过滤器无法删除key
删除需要把对应 key 的 hash 所对应的 二进制数都变为0;
但是这个二进制数有可能不止是这一个key所指向的,见上图二进制数组中的第二个1,同时被两个箭头所指;
如果在数据库中删除 subCate:1 这个key时,将布隆过滤器在对应的全部二进制改为0,那么 subCate:1 也在布隆过滤器中被判定为不存在了;
3.5 SpringBoot使用布隆过滤器
添加 maven 依赖 :
<!-- 布隆过滤器 依赖 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
测试代码
public class BloomFilterTest {
@Test
public void test(){
// 布隆过滤器创建 其中0.01为fpp:误判率 可降低误判率
BloomFilter bf = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")),
100000, 0.01);
for(int i = 0; i < 100000; i++){
bf.put(String.valueOf(i));
}
int counts = 0;
for(int i = 0; i < 1000; i++){
boolean isExist = bf.mightContain("didiok" + i);
if (isExist) {
counts++;
}
}
System.out.println("误判个数" + counts);
}
}
二、缓存雪崩
某个时间点大面积的key失效,刚好又有很大的流量涌入,所有的请求都打在数据库,数据库的压力就非常大可能会宕机,这就是缓存雪崩。
缓存雪崩没有好的解决方案,重在预防发生雪崩。
预防方案:
-
永不过期 如果需要对某些key的数据进行更新的话,可以采用手动过期
-
过期时间错开 缓存的key, 过期时间错开
-
多种缓存结合 比如redis加上memcache, 并将memcache的过期时间设置的比redis长一些
-
采购第三方Redis 购买高可用的redis主从备份