缓存穿透、击穿、雪崩到底是啥

相信了解过缓存的伙伴们,也应该也经常听到“缓存穿透、缓存击穿、缓存雪崩”这几个词。最近笔者在实际开发过程中也使用了缓存,也理解到这些是我们在使用缓存的时候都要去考虑的问题,本文就围绕这几个词给大家展开分析下。

缓存穿透

定义

是指当查询一个一定不存在的数据时,则会绕过缓存,直接请求到DB,当访问流量过大时,会导致DB挂掉。

解决方案
  • 如果访问的key未能在DB查询到对应的值,也将空值写进缓存,但可以设置较短过期时间。 这样可以减少不存在的数据对DB进行无效的查询,从而减少DB的压力。
  • 采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤,从而避免了对DB的查询;
布隆过滤器

定义

本质上布隆过滤器是一种数据结构,概率型数据结构(probabilistic datastructure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。相比于传统的 List、Set、Map等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。底层数据结构是一个位数组,数组最大的长度为2的32次方,512M。位数组其实每位只占用一个位(0或者1),正常业务大致申请10000元素的位,也只需要1250B的空间,性能和内存占用方面不成问题。

原理
布隆过滤器原理
当要判断一个值是否在布隆过滤器中,对元素进行多次哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。

缓存击穿

定义

一个存在的热点key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。简单来说就是热点key失效期间刚好有大量并发请求过来,导致DB压力增大。

解决方案
  • 使用分布式锁
    在缓存失效的时候(判断拿出来的值为空),采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法,代码实现如下:
public String get(key) {

    String value = redis.get(key);

    if (value == null) { //代表缓存值过期
        //设置2min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
        if (redis.setnx(key_mutex, 1, 2 * 60) == 1) {  //代表设置成功
            value = db.get(key);
            redis.set(key, value, expire_secs);
            redis.del(key_mutex);
        } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
            sleep(60);
            get(key);  //重试
        }
    } else {
        return value;      
    }
}

  • 设置“永不过期”锁。确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期;从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期。这种方式还是非常友好的,唯一不足的就是构建缓存时候,其余线程可能访问的是老数据。大致代码如下:
String get(final String key) {  
    V v = redis.get(key);  
    String value = v.getValue();  
    long timeout = v.getTimeout();  
    if (v.timeout <= System.currentTimeMillis()) {  
        // 异步更新后台异常执行  
          threadPool.execute(new Runnable() {  
              public void run() {  
                  String keyMutex = "mutex:" + key;  
                  if (redis.setnx(keyMutex, "1")) {  
                        // 3 min timeout to avoid mutex holder crash  
                        redis.expire(keyMutex, 3 * 60);  
                        String dbValue = db.get(key);  
                        redis.set(key, dbValue);  
                        redis.delete(keyMutex);  
                  }  
              }  
          });  
    }  
    return value;  
}

缓存雪崩

定义

给缓存设置了同样的过期时间,导致缓存在某一时刻同时生效,所有请求一并到DB,DB瞬间压力增大,引起雪崩

解决方案

通过定义也知道,导致雪崩的主要原因是设置了相同的过期时间,可以在给缓存设置过期时间是可以加上一个随机时间,让不同的key的过期时间错开,减少在同时失效的概率

以上是本次为大家分享内容,欢迎评论指点!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值