redis面试篇04_缓存击穿、穿透、雪崩

150 阅读5分钟

1、什么是缓存击穿?

缓存击穿跟缓存雪崩有点类似,缓存雪崩是大规模的Key失效,缓存击穿是某个热点的Key失效,大并发集中对其进行请求,就会造成大量请求读缓存没读到数据,从而导致高并发请求访问数据库,引起数据库压力剧增。这种现象就叫做缓存击穿。

从两个方面解决,第一是否可以考虑热点Key不设置过期时间,第二是否可以考虑降低打在数据库上的请求数量。

解决方案:

  • 热点数据缓存永远不过期,物理不过期,针对热点Key不设置过期时间;逻辑过期,把过期时间存在Key对应的value里,如果发现过期了,通过后台的异步线程进行缓存的构建。
  • 在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如说某个Key只允许一个线程查询数据和写缓存,其他线程等待(这种方式会阻塞其他的线程,系统的吞吐量会下降)。

2、什么是缓存穿透?

缓存穿透是指用户请求的数据不在缓存中,同时也不在数据库中,导致用户每次请求该数据都要去数据库中查一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量的请求落在数据库中,从而造成数据库压力过大,甚至可能导致数据库承受不住压力而宕机。

缓存穿透的关键在于Redis中查不到的Key值,它和缓存击穿的根本区别在于传进来的Key在Redis中是不存在的。假如黑客传进大量的不存在的Key,那么大量的请求就会打在数据库上,这是很致命的问题哦。所以日常开发中要对参数做好校验,一些非法参数直接返回错误提示。

解决方法:

  • 将无效的Key存放到Redis中,并设置上较短的过期时间。

当出现Redis查不到数据,数据库也查不到数据的情况,我们可以将这个Key保存到Redis中,将其value值为null,并设置较短的过期时间,后面再出现查询这个Key的请求时,会直接返回null,就不再用查询数据库了。这种处理方式也是有问题的,假设传进来的不存在的Key值每次都是随机的,那存进Redis也没有意义了。

  • 使用布隆过滤器:

如果布隆过滤器判定某个Key不存在布隆过滤器中,那么就一定不存在,如果判定某个Key存在,那么它就大概率存在(有一定误判概率)。于是我们可以在缓存之前再加一个布隆过滤器,将数据库中所有Key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查一下Key是否存在,不存在就直接返回,不让其访问数据库,从而避免其对底层存储系统的查询压力。

如何选择:

针对一些恶意攻击,攻击带过来的大量Key是随机的,那么建议使用布隆过滤器过滤掉这些key;对于空数据的Key有限,重复率较高的,则可以采用缓存空数据来应对。

3、什么是缓存雪崩?

如果缓存在某一时刻出现大规模的key失效,导致大量的请求打到数据库上,导致数据库压力巨大,如果高并发的情况下,可能会瞬间将数据库打宕机。如果这时候运维马上重启数据库,马上又会有新的流量将数据库打死。这就是缓存雪崩。

造成缓存雪崩的关键是在于大规模的Key失效,主要有2种可能:一是Redis宕机了;二是可能采用了相同的过期时间。

解决方案:

1、事前:

  • 设置不同的过期时间,让缓存失效的时间尽量均匀,避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。比如每个Key的过期时间都加一个随机值,保证数据不会在同一时间大规模的失效。
  • 分级缓存:第一级缓存失效的情况下访问第二级缓存,每一级缓存的失效的时间都不相同。
  • 热点数据缓存不设置过期时间,而是设置逻辑过期时间,将过期时间存在对应Key的value里,如果发现要过期了,通过后台的一个异步线程进行缓存的构建。
  • 保证Redis缓存的高可用,防止Redis宕机导致的缓存雪崩问题。可以使用主从+哨兵,Redis集群来避免Redis全盘崩溃的情况。

2、事中:

  • 互斥锁来控制读数据写缓存的线程数量,比如说某个Key只允许一个线程查询数据和写缓存,其他线程等待(这种方式会阻塞其他的线程,系统的吞吐量会下降)。
  • 使用熔断机制,限流降级。当流量到达一定的阈值,直接返回“系统拥挤”之类的提示,防止过多的请求打到数据库上将数据库击垮,至少保证了一部分用户是可以正常使用的,其他用户多刷新几次也是能得到结果。

3、事后:

  • 开启Redis的持久化机制,尽快恢复缓存数据,一旦重启就能从磁盘上自动加载数据恢复内存中的数据。