Redis入门:七、缓存击穿和缓存穿透以及雪崩

一、前言

在正式讲解这三种异常时,我们先话两分钟来回顾一下,redis缓存或者说一般缓存的基本流程,老习惯,开局一张图,剩下全靠编,啊呸,剩下全靠理解。
在这里插入图片描述

二、缓存击穿以及应对策略

1、定义

假设有一个热点新闻K-V在缓存数据库中,比如说微博热搜,但是当微博热搜的K-V过期,则会透过缓存数据库,直接打到数据库中,造成一瞬间有大量用户访问,给数据库带来很大的压力。
在这里插入图片描述

2、应对策略

2.1使用互斥锁

并发量不大的时候可以使用以下方法实现:
简单思路分析:由于缓存击穿是由于用户访问同一个热点K-V,如果刚好该热点失效,则让一个用户即单线程去数据库中获取,然后再更新缓存,未获取到线程的用户进行排队,再从缓存中进行读取热点K-V,如果缓存更新,则不用再读数据库,如果未更新,则等待一会,循环上述步骤。

static Lock reenLock = new ReentrantLock();

public List<String> getData() throws InterruptedException {
  List<String> result = new ArrayList<String>();
  // 从缓存读取数据
  result = getDataFromCache();
  if (result.isEmpty()) {
      if (reenLock.tryLock()) {
	      try {
	           System.out.println("拿到锁,从数据库中读取数据!");
	           // 从数据库查询数据
	           result = getDataFromDB();
	           // 将查询到的数据写入缓存
	           setDataToCache(result);
          } finally {
              reenLock.unlock();// 释放锁
          }

      } else {
          // 再查一下缓存是否有
          result = getDataFromCache();
          if (result.isEmpty()) {
              System.out.println("未拿到锁,等待一会");
              Thread.sleep(100);
              // 重试
              return getData();
          }
      }
  }
  return result;
}

并发量较大时,可以用redis分布式锁操作:

public String get(String key){
    String value = redis.get(key);

    //如果查询不到目标值
    if(StringUtils.isNullOrEmpty(value)) {
        //其实这是一个加锁的过程
        if (redis.setnx(key_mutex,1)==1) {
        	//从数据库中获取数值
            value = getDataFromDB(key);
            //再放入缓存中
            redis.set(key,value);
            //释放锁
            redis.del(key_mutex);
        } else {
          value = redis.get(key);
          if(StringUtils.isNullOrEmpty(value)) {
             Thread.sleep(100);
             return get(key);
          }
        }
    }
    return value;
}

2.2 设置永不过期

在商城里面,对于一些爆款,可以设置该产品K-V用不过期,防止出现击穿现象。
总结: 具体使用哪种方式进行处理,还是需要根据具体的业务场景进行设置,并非绝对的。

三、缓存穿透及应对策略

1、定义

缓存穿透是指用户访问缓存时,携带一个缓存中不存在的key,比如说ID为-1.此时在缓存无法找到对应的K-V,则会去数据库中查找。如果此时刚好有大量用户都查找这个key,则相当于穿过或者绕过缓存直接访问数据库,给数据库造成压力。
在这里插入图片描述

2、应对策略

缓存空对象

简单说,就是当查询的key不存在对应值时,返回一个空值,但是在高并发量时,会存在大量的空值,占用内存空间,此时,我们可以对空值设置过期时间,但是该做法依然存在弊端,在有效期内数据很可能不一致。

布隆过滤器:

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
涉及到具体原理就不讲了,能力有限,自行观看。
数学之美:布隆过滤器
布隆过滤器牛逼地方在于非常高效同时占空间非常少,它判断一个元素不存在那肯定就是不存在,它判断存在的时候有一定误差,是有可能不存在的。比如服务端收到请求之后,可以首先用布隆过滤器判断下用户名是否存在,如果不存在就直接返回,就不用再去查DB了,非常完美的解决方案!


布隆过滤器的具体使用方法就放到其他博客文章进行详细讲解了。

四、雪崩及应对场景

1、定义

在这里插入图片描述
还是可以借鉴之前那个图,但是不同之处就是,在缓存中,同一时刻,存在大量的K-V过期,导致用户直接访问到数据中去了。从而给数据库造成很大的压力,甚至宕机。

2、解决方法

1.缓存数据的过期时间设置为随机
2.针对不同的业务场景设置不同的过期时间加随机时间,比如热点永不过期。

猜你喜欢

转载自blog.csdn.net/weixin_39085109/article/details/106679889