Redis缓存穿透和雪崩

一、什么是缓存穿透?

定义:

客户端查询了一个数据库中没有的记录导致缓存在这种情况下无法利用,称之为缓存穿透或者缓存击穿。

image.png

如图,这是提条正常的查询语句,客户端查询id为“1”的这样一条记录,这条记录在数据库中,并且在第一次查询的时候会将记录放在redis缓存中,在之后的查询中,直接从缓存拿。那如果此时需要查询id为 “-1”的记录,这个记录不在数据库,因此频繁的请求id=“-1”,导致缓存失效,所有的请求都涌向数据库,这样就出现了咱们所说的缓存穿透。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

 

解决办法

1.结合mybatis中的cache

将查询的没有在数据库的记录,返回null值存放在redis缓存,有效的避免了缓存穿透。

 

2.redis分布式锁

查询一条记录,如果没有在缓存中,则查询redis,redis中没有,则给这个代码块添加分布式锁,让一个线程去查记录,将查出来的数据存放的redis和redis缓存,其他线程查的时候直接在缓存中查,有效避免了所有查询都走向数据库,给数据库造成太大压力。

 

3.redis双重检测

image

  • 再web服务器启动时,提前将有可能被频繁并发访问的数据写入缓存。—这样就规避大量的请求在第3步出现排队阻塞。
  • 规范key的命名,并且统一缓存查询和写入的入口。这样,在入口处,对key的规范进行检测。–这样保存恶意的key被拦截。
  • Synchronized双重检测机制,这时我们就需要使用同步(Synchronized)机制,在同步代码块前查询一下缓存是否存在对应的key,然后同步代码块里面再次查询缓存里是否有要查询的key。 这样“双重检测”的目的,还是避免并发场景下导致的没有意义的数据库的访问(也是一种严格避免穿透的方案)。
  • 这一步会导致排队,但是第一步中我们说过,为了避免大量的排队,可以提前将可以预知的大量请求提前写入缓存。
  • 不管数据库中是否有数据,都在缓存中保存对应的key,值为空就行。–这样是为了避免数据库中没有这个数据,导致的平凡穿透缓存对数据库进行访问。
  • 第4步中的空值如果太多,也会导致内存耗尽。导致不必要的内存消耗。这样就要定期的清理空值的key。避免内存被恶意占满。导致正常的功能不能缓存数据。

 

 

4.采用redis布隆过滤器

将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

二、什么是缓存雪崩?

定义:

在系统运行的某一时刻,突然系统中缓存全部失效,恰好在这一时刻,涌来大量的客户端请求,导致所有模块缓存无法利用,大量的请求涌向数据库导致极端情况,导致数据库、CPU和内存负载过高,甚至宕机。

缓存雪崩是指Redis中大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,此时数据库层的负载压力会骤增,我们称这种现象为"缓存雪崩"。事实上缓存雪崩相比于缓存击穿更容易发生,对于大多数公司来讲,同时超大并发量访问同一个过时key的场景的确太少见了,而大量key同时过期,大量用户访问这些key的几率相比缓存击穿来说明显更大。

高并发导致雪崩

解决方案:

1.缓存永久存储[不推荐]

2.针对不同的业务数据一定要设置不同超时时间

3.双缓存方案

        主缓存:有效期按照经验值设置,主要读取的缓存,主缓存失效后从数据库加载最新值。

        备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。

        其实就是缓存降级策略。

        代码实例:

BigDecimal orderAmount = cs.cacheResult(userCode,CACHE_NAME);

if( orderAmount  !=null){

        return  orderAmount  ;

}

if(lock.tryLock()){

          BigDecimal orderAmount = mapper.getOrderAmount(userCode);

          cs.cachePut( userCode , orderAmount  ,  CACHE_NAME );

          cs.cachePut( userCode+"_back" , orderAmount  ,  CACHE_NAME_BACK );

          lock.releaseLock();

          return  orderAmount  ;

}else{

          return  cs.cacheResult( userCode+"_back" , CACHE_NAME_BACK  );

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值