缓存穿透与雪崩

文章介绍了缓存穿透问题,即查询数据库中不存在的数据导致的频繁数据库请求,提出解决方案——使用布隆过滤器,即使得空数据也能被过滤掉。布隆过滤器存在误判但能有效减少数据库压力。另外,文章提到了缓存雪崩的问题,指出预防措施如永不过期、过期时间错开等。最后,展示了在SpringBoot中使用Guava实现布隆过滤器的示例。
摘要由CSDN通过智能技术生成

一、缓存穿透

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主从备份

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值