如何解决Redis缓存雪崩击穿与穿透

Posted 肥肥技术宅

tags:

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

Redis最常用使用的场景就是作为业务系统的缓存,既然是作为缓存,那么就不免会碰到缓存常见的问题,即雪崩击穿穿透,什么是缓存雪崩、击穿与穿透以及如何解决这几个问题呢?今天我们一起来探讨一下!

一、缓存雪崩

1. 什么是缓存雪崩?

缓存雪崩是指大量的请求无法命中Redis中的缓存数据,也就是在Redis找不到数据了,那业务系统只能到数据库中查询,进而导致所有的请求都发送到了数据库。如下图所示:

 

数据库并不像Redis能处理大量请求,由缓存雪崩导致的请求激增必须会导致数据库所在宕机,这样势必会影响业务系统,所以如果发生缓存雪崩,对于业务系统肯定是致命的。

2. 为什么发会生缓存雪崩?

什么情况下出现缓存雪崩呢?总结起来有以下两个方面的原因:

  • 大量Redis缓存数据同时过期,导致所有的发送到Redis请求都无法命中数据,只能到数据库中进行查询。

  • Redis服务器宕机,所有请求都无法经Redis来处理,只能转向数据库查询数据。

3. 如何避免缓存雪崩?

针对导致缓存雪崩的原因,有不同的解决方法:

  • 针对大量缓存随机过期时间,解决方法就是在原始过期时间的基础上,再加一个随机过期时间,比如1到5分钟之间的随机过期时间,这样可以避免大量的缓存数据在同一时间过期。

  • 而针对Redis解决宕机的导致的缓存雪崩,可以提前搭建好Redis的主从服务器进行数据同步,并配置哨兵机制,这样在Redis服务器因为宕机而无法提供服务时,可以由哨兵将Redis从服务器设置为主服务器,继续提供服务。

二、缓存击穿

1. 什么是缓存击穿

缓存击穿与缓存雪崩的情况相似,雪崩是因为大量的数据过期,而缓存击穿则是指热点数据过期,所有针对热点数据的请求都需要到数据库中进行处理,如下图所示:

 

2. 怎么避免缓存击穿?

解决缓存击穿的三种方式:

  • 不设置过期时间

如果我们能提前知道某个数据是热点数据,那么就可以不设置这些数据的过期,从而避免缓存击穿问题,比如一些秒杀活动的商品,在秒杀时会大量用户访问,这时候我们就可以将这些用于秒杀的商品数据提前写入缓存并且不设置过期时间。

  • 互斥锁

提前知道某些数据会有大量访问,我们当然可以设置不过期,但更多时候,我们并不能提前预知,这种情况要怎么处理呢?

我们来分析一下缓存击穿的情况:

正常情况下,当某个Redis缓存数据过期时,如果有对该数据的请求,则重新到数据库中查询并再写入缓存,让后续的请求可以命中该缓存而无须再去数据库中查询。

而热点数据过期时,由于大量请求,当某个请求无法命中缓存时,会去查询数据库并重新把数据写入Redis,也就是在写入Redis之前,其他请求进来,也会去查询数据库。

好了,我们知道热点数据过期后,很多请求会去查询数据库,那么我们可以给去查询数据库的业务逻辑加个互斥锁,只有获得锁的请求才能去查询数据库并把数据写回Redis,而其他没有获得锁的请求只能等待数据就绪。

上述步骤的如下图所示:

 

  • 设置逻辑过期时间

使用互斥锁虽然可以非常简单地解决缓存击穿问题,但没有获得锁的请求虽然排队等待,这样影响了系统的性能,还有另一种解决缓存击穿的方法就是在业务数据冗余一个过期时间,比如下面的数据中我们增加了expire_at字段用于表示数据过期时间。

"name":"test","expire_at":"1599999999"
复制代码

这种方式的实现过程如下图所示:

 

缓存中的热点数据中冗余一个逻辑过期时间,但数据在Redis不设置过期时间

当一个请求拿到Redis中的数据时,判断逻辑过期时间是否到期,如果没有到期,直接返回,如果到期则开启另一个线程获得锁后去查询数据库并将查询的最新数据写回Redis,而当前请求返回已经查询的数据。

三、缓存穿透

1. 什么是缓存穿透

缓存穿透是指要查找的数据既不在缓存当中,也不在数据库中,因为不在缓存中,所以请求一定会到达数据库,Redis缓存形同虚设,如下图所示:

 

2. 为什么会发生缓存穿透

什么条件下会发生缓存穿透呢?主要有以下三种情况:

  • 用户恶意攻击请求

  • 误操作把Redis和数据库里的数据删除了

  • 用户还未产生内容时,比如用户的文章列表,用户还未写文章,所以缓存和数据库都没有数据

3. 如何避免缓存穿透?

a. 缓存空值或缺省值

当在Redis缓存中查询不到数据时,再从数据库查询,如果同样没有数据,就直接缓存一个空间或缺省值,这样可以避免下次再去查询数据库;不过为了防止之后已经数据库已经相应数据库,再返回空值问题,应该为缓存设置过期时间,或者在产生数据时直接清除对应的缓存空值。

b. 布隆过滤器

虽然缓存空值可以解决缓存穿透问题,但仍然需要查询一次数据库才能确定是否有数据,如果有用户恶意攻击,高并发地使用系统不存在的数据id进行查询,所有的查询都要经过数据库,这样仍然会给数据库带来很大的压力。

所以,有没有不用查询数据库就能确定数据是否存在的办法呢?有的,用布隆过滤器

布隆过滤器主要是两个部分:bit数组+N个哈希函数,其原理为:

  • 使用N个哈希函数对所要标记的数据进行哈希值计算。

  • 将计算到的哈希值对bit数组的长度取模,这样可以得到每个哈希值在bit数组的位置。

  • 把bit数组中对应的位置标记为1。

下面是布隆过滤器原理示意图:

 

当要进行数据写入时,执行述述步骤,计算对应bit数组位置并标识为1,那么在执行查询时,就能查询该数据是否存在了。

另外,由于哈希碰撞问题导致的误差,所以不存在的数据经过布隆过滤器后,会被判定为存在,再去查数据库,不过哈希碰到的概率很小,用布隆过滤器已经能帮我们拦截大部分的穿透请求了。

Redis本身就支持布隆过滤器,所以我们可以直接使用Redis布隆过滤器,而不用自己去实现,非常方便。

四、小结

缓存的雪崩、击穿、穿透是在业务应用缓存时经常会碰到的缓存异常问题,其原因与解决方法如以下表示所示:

问题原因解决方法
缓存雪崩大量数据过期或Redis服务器宕机1. 随机过期时间 2. 主从+哨兵的集群
缓存击穿热点数据过期1. 不设置过期时间 2. 加互斥锁 3. 冗余逻辑过期时间
缓存穿透请求数据库和Redis都没有的数据1. 缓存空值或缺省值 2. 布隆过滤器

穿透击穿雪崩…Redis这么多问题,如何解决

本文分享自华为云社区《【高并发】什么是缓存穿透?击穿?雪崩?如何解决?》,作者:冰 河。

说到Redis,往往更多的场景是被用作系统的缓存,说到缓存,尤其是分布式缓存系统,在实际高并发场景下,稍有不慎,就会造成缓存穿透、缓存击穿和缓存雪崩的问题。那什么是缓存穿透?什么是缓存击穿,又什么是缓存雪崩呢?它们是如何造成的?又该如何解决呢?今天,我们就一起来探讨这些问题。

缓存穿透

首先,我们来说说缓存穿透。什么是缓存穿透呢?缓存穿透问题在一定程度上与缓存命中率有关。如果我们的缓存设计的不合理,缓存的命中率非常低,那么,数据访问的绝大部分压力都会集中在后端数据库层面。

什么是缓存穿透?

如果在请求数据时,在缓存层和数据库层都没有找到符合条件的数据,也就是说,在缓存层和数据库层都没有命中数据,那么,这种情况就叫作缓存穿透。

我们可以使用下图来表示缓存穿透的现象。

 

造成缓存穿透的主要原因就是:查询某个Key对应的数据,Redis缓存中没有相应的数据,则直接到数据库中查询。数据库中也不存在要查询的数据,则数据库会返回空,而Redis也不会缓存这个空结果。这就造成每次通过这样的Key去查询数据都会直接到数据库中查询,Redis不会缓存空结果。这就造成了缓存穿透的问题。

如何解决缓存穿透问题?

既然我们知道了造成缓存穿透的主要原因就是缓存中不存在相应的数据,直接到数据库查询,数据库返回空结果,缓存中不存储空结果。

那我们就自然而然的想到了第一种解决方案:就是把空对象缓存起来。当第一次从数据库中查询出来的结果为空时,我们就将这个空对象加载到缓存,并设置合理的过期时间,这样,就能够在一定程度上保障后端数据库的安全。

第二种解决缓存穿透问题的解决方案:就是使用布隆过滤器,布隆过滤器可以针对大数据量的、有规律的键值进行处理。一条记录是不是存在,本质上是一个Bool值,只需要使用 1bit 就可以存储。我们可以使用布隆过滤器将这种表示是、否等操作,压缩到一个数据结构中。比如,我们最熟悉的用户性别这种数据,就非常适合使用布隆过滤器来处理。

缓存击穿

如果我们为缓存中的大部分数据设置了相同的过期时间,则到了某一时刻,缓存中的数据就会批量过期。

什么是缓存击穿?

如果缓存中的数据在某个时刻批量过期,导致大部分用户的请求都会直接落在数据库上,这种现象就叫作缓存击穿。

我么可以使用下图来表示缓存击穿的线程。

造成缓存击穿的主要原因就是:我们为缓存中的数据设置了过期时间。如果在某个时刻从数据库获取了大量的数据,并设置了相同的过期时间,这些缓存的数据就会在同一时刻失效,造成缓存击穿问题。

如何解决缓存击穿问题?

对于比较热点的数据,我们可以在缓存中设置这些数据永不过期;也可以在访问数据的时候,在缓存中更新这些数据的过期时间;如果是批量入库的缓存项,我们可以为这些缓存项分配比较合理的过期时间,避免同一时刻失效。

还有一种解决方案就是:使用分布式锁,保证对于每个Key同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,其他线程没有获得分布式锁的权限,需要进行等待。不过在高并发场景下,这种解决方案对于分布式锁的访问压力比较大。

缓存雪崩

如果缓存系统出现故障,所有的并发流量就会直接到达数据库。

什么是缓存雪崩?

如果在某一时刻缓存集中失效,或者缓存系统出现故障,所有的并发流量就会直接到达数据库。数据存储层的调用量就会暴增,用不了多长时间,数据库就会被大流量压垮,这种级联式的服务故障,就叫作缓存雪崩。

我们可以用下图来表示缓存雪崩的现象。

造成缓存雪崩的主要原因就是缓存集中失效,或者缓存服务发生故障,瞬间的大并发流量压垮了数据库。

如何解决缓存雪崩问题?

解决缓存雪崩问题最常用的一种方案就是保证Redis的高可用,将Redis缓存部署成高可用集群(必要时候做成异地多活),可以有效的防止缓存雪崩问题的发生。

为了缓解大并发流量,我们也可以使用限流降级的方式防止缓存雪崩。例如,在缓存失效后,通过加锁或者使用队列来控制读数据库写缓存的线程数量。具体点就是设置某些Key只允许一个线程查询数据和写缓存,其他线程等待。则能够有效的缓解大并发流量对数据库打来的巨大冲击。

另外,我们也可以通过数据预热的方式将可能大量访问的数据加载到缓存,在即将发生大并发访问的时候,提前手动触发加载不同的数据到缓存中,并为数据设置不同的过期时间,让缓存失效的时间点尽量均匀,不至于在同一时刻全部失效。

点击关注,第一时间了解华为云新鲜技术~

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

Redis三大缓存问题(穿透击穿雪崩)

Redis缓存雪崩缓存穿透缓存击穿

穿透击穿雪崩…Redis这么多问题,如何解决

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

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

Redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略