【缓存】缓存穿透/缓存击穿/缓存雪崩

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/nakiri_arisu/article/details/88859125

缓存穿透

概念

缓存的作用就是将一些可能的热点数据存起来放在DB访问的前面,减少对于DB的查询操作减轻DB的压力。
在这里插入图片描述
而缓存穿透就是我故意查询一些不命中缓存的东西,让他去查询DB。就像这层缓存完全不存在,被穿透了一样

解决方案

  1. 每次查询这种数据库不存在的key,就将这个key和其对应的null值缓存起来,下次查询的时候直接在缓存层返回null。缺点: 如果大范围的key去扫数据库的话,可能会占用缓存非常多的key(比如整1000W个无意义的key去不停查),建议这种瞎几把查的key设短一点的过期时间。 适用范围,key值范围比较小的那种
  2. 比较推荐的一种是在缓存层之前再设一个BloomFilter。先在BloomFilter里找一下此次查询的key是否存在,不存在直接返回,存在的话再从缓存中查询,缓存没有再查DB
    在这里插入图片描述
    这里理解一下,BloomFilter要对每一个数据库的值进行涂黑操作,数据库有新值注入的时候就要进行涂黑。
 String get(String key) {
     if (!bloomfilter.mightContain(key)) { // 恶意数据
         return null;
     } else {
         String value = redis.get(key);
         if (null == value) { // 缓存没有就查库+装填缓存
             value = db.get(key);
             redis.set(key, value);
         }
         return value;
     }
 }

缓存击穿

概念

听起来和缓存穿透差不多,但是还是有区别的。
缓存穿透强调的是绕过了缓存,缓存击穿强调的是原本缓存应该是命中的,但是由于缓存自身的原因现阶段查不到该key(或者大范围的key)的值,从而击穿到了DB。

而且缓存击穿往往伴随着那个击穿的key可能会被高频的查询从而导致DB的压力猛然上升。

解决方案

使用互斥锁

单机的通过lock就行,集群环境使用分布式锁。具体做法在根据key获得的value值为空时,先锁上,再从数据库加载,加载完毕,释放锁。若其他线程发现获取锁失败,则睡眠50ms后重试。

集群环境redis代码示例:

 String get(String key) {
     String value = redis.get(key);
     if (value  == null) {
         if (redis.setnx(key_mutex, "1")) {
             // 3 min timeout to avoid mutex holder crash
             redis.expire(key_mutex, 3 * 60);
             value = db.get(key);
             redis.set(key, value);
             redis.delete(key_mutex);
         } else {
             //其他线程休息50毫秒后重试
             Thread.sleep(50);
             get(key);
         }
     }
     return value;
 } 

异步构建缓存

在这种方案下,构建缓存采取异步策略,会从线程池中取线程来异步构建缓存,从而不会让所有的请求直接怼到数据库上。

缓存雪崩

概念

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,造成数据库后端故障,从而引起应用服务器雪崩。

解决方案

  1. 避免缓存集中失效,不同的key设置不同的超时时间
  2. 增加互斥锁,控制数据库请求,重建缓存。
  3. 提高缓存的HA,如:redis集群。
展开阅读全文

没有更多推荐了,返回首页