Redis缓存穿透和雪崩

Posted 似水流年,是谁苍白了等待

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis缓存穿透和雪崩相关的知识,希望对你有一定的参考价值。

一、什么是缓存穿透?

定义:

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

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

 

解决办法

1.结合mybatis中的cache

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

 

2.redis分布式锁

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

 

3.redis双重检测

  • 再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  );

 

以上是关于Redis缓存穿透和雪崩的主要内容,如果未能解决你的问题,请参考以下文章

Redis缓存雪崩和缓存穿透缓存预热缓存降级

大厂Redis缓存雪崩,穿透,击穿,降级,预热等解决方案,面试官想知道的都在这!

缓存击穿、穿透、雪崩及Redis分布式锁

Redis学习笔记10:Redis缓存穿透和雪崩

Redis学习笔记10:Redis缓存穿透和雪崩

Redis学习笔记10:Redis缓存穿透和雪崩