likes
comments
collection

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

作者站长头像
站长
· 阅读数 14

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

一、缓存雪崩

1. 什么是缓存雪崩?

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

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

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

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

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

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

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

3. 如何避免缓存雪崩?

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

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

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

二、缓存击穿

1. 什么是缓存击穿

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

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

2. 怎么避免缓存击穿?

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

  • 不设置过期时间

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

  • 互斥锁

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

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

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

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

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

上述步骤的如下图所示:

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

  • 设置逻辑过期时间

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

{"name":"test","expire_at":"1599999999"}

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

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

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

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

三、缓存穿透

1. 什么是缓存穿透

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

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

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

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

  • 用户恶意攻击请求

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

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

3. 如何避免缓存穿透?

a. 缓存空值或缺省值

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

b. 布隆过滤器

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

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

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

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

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

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

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

【说透Redis】如何解决Redis缓存雪崩、击穿与穿透

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

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

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

四、小结

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

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