之前的文章我们详细讲解了Redis的三种集群模式,这篇文章我们来讲解一下Redis缓存异常的三种情况。
缓存击穿、缓存穿透、缓存雪崩这三个问题是Redis在实际项目中会经常遇到的问题,首先我们来看一下Redis在项目中作为缓存中间件是如何工作的:

客户端发起一个查询请求的时候,首先去缓存中查询,如果数据在缓存中存在,则直接将缓存中的数据返回给客户端;如果数据在缓存中不存在,则继续查询数据库,如果数据在数据库中存在,则将该数据放入缓存中,并返回给客户端,如果数据也不在数据库中,则直接返回null给客户端。
缓存击穿
缓存击穿是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。这种情况会导致数据库的压力骤增,造成大量请求阻塞,甚至直接挂掉。

解决方法
可以从两个方面考虑,一方面可以考虑热点Key不设置过期时间,另一方面可以考虑降低打在数据库上的请求数量。
第一种方案,可以设置热点Key永不过期。永不过期有两层意思:第一是物理上的永不过期,也就是不对热点Key设置过期时间;第二是逻辑不过期,即正常的给热点Key设置过期时间,但是后台会开启一个定时任务定期去更新缓存。
第二种方案,通过使用分布式锁来限制打在数据库上的请求数量。当大量请求查询同一个Key时,由于只有一个请求可以获得锁,所以实际打到数据库上的请求只有一条,这条请求拿到数据后会将其放入缓存中,此时,其余处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库。
缓存穿透
缓存穿透是指查询一个缓存和数据库中都不存在的数据,导致每次查询这条数据都会穿过缓存,直接查询数据库,最后返回空。当用户使用这条不存在的数据疯狂发起查询请求的时候,对数据库造成的压力就非常大,甚至可能直接挂掉,具体的流程如下所示:

解决方法
解决缓存穿透的方法一般有两种,第一种是缓存空对象,第二种是使用布隆过滤器。
缓存空对象
当数据库中查不到数据时,我缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次查询该数据的时候,就可以直接从缓存中拿到,从而达到了减小数据库压力的目的。但是这种解决方法有两个缺点:
(1)需要提供更多的内存空间来缓存这些空对象,当这种空对象很多时,就会浪费巨大的内存空间。
(2)缓存空对象会导致缓存中的数据与数据库中的数据不一致,并且在缓存中缓存很多数据库中不存在的数据本身就有很不合理。
使用布隆过滤器
由于缓存空对象的方法弊端比较多,多以使用布隆过滤器的方法是比较推荐的。布隆过滤器,其实就是一种数据结构,它是由一个长度为mbit的数组与n个哈希函数组成的数据结构,位数组中每个元素的初始值都是0,。在初始化布隆过滤器时,会先将所有key进行n次哈希运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1。这样,就相当于把所有的Key都保存到布隆过滤器中了。
举个例子,假设我们一共有3个Key,我们对这3个Key分别进行3次哈希运算,Key1经过3次哈希运算后1、3、5,那么布隆过滤器中下标为1、3、5的元素值更新为1,然后对剩下的2个Key做同样的操作,结果如下图:

当客户端查询时,也对查询的Key做3次哈希运算得到3个位置,然后看布隆过滤器中对应位置元素的值是否为1,如果所有对应位置元素的值都为1,那么就证明Key在库中存在,则继续向下查询;如果3个位置中有任意一个位置的值不为1,则说明Key在库中不存在,直接返回给客户端空即可。所以,布隆过滤器就相当于一个位于客户端和缓存层之间的拦截器一样,负责判断key是否存在于集合中。

布隆过滤器的优点是解决了缓存空对象方法存在的缺点,但是布隆过滤器本身也有缺点。首先,布隆过滤器存在误判的可能,如果一个查询的键被布隆过滤器判定为存在于库中,那么这个键有一定的概率不存在于库中(精度与布隆过滤器的数组长度有关),但是被布隆过滤器判定为不存在的键就一定不存在于库中。其次是删除元素比较困难,因为删除一个元素时,其通过计算得到的哈希值对应布隆过滤器上的下标的值应该被清0,但是这可能会影响其余Key的判断。
缓存雪崩
缓存雪崩是指当缓存中有大量的Key在同一时刻过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成数据库查询压力骤增,甚至直接挂掉。

解决方法
发生缓存雪崩之前:
(1)均匀过期。设置不同的过期时间,让缓存失效的时间尽量均匀,避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。
(2)分级缓存。第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。
(3)采用热点数据永不过期的策略。
(4)采用Redis集群来保证缓存的高可用,防止Redis宕机造成的缓存雪崩。
发生缓存雪崩时:
(1)加分布式锁。在缓存失效后,通过加分布式锁的方式来减少打到数据库上的请求量,防止数据库被打崩。
(2)使用熔断机制,限流降级。当流量达到一定的阈值后,直接返回客户端提示而不走数据库查询,以此来减轻数据库的压力。
发生缓存雪崩后:
(1)开启Redis数据持久化机制,尽快恢复缓存数据,一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。
这篇文章讲解了三种缓存异常的问题,大家有什么问题或者勘误可以在评论区留言,笔者看到都会回复的。
614

被折叠的 条评论
为什么被折叠?



