Redis——缓存穿透缓存击穿缓存雪崩
Posted shog808
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis——缓存穿透缓存击穿缓存雪崩相关的知识,希望对你有一定的参考价值。
一、缓存穿透
1、含义
缓存穿透是指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。
2、解决方案
1)缓存空对象
就是当数据库中查不到数据的时候,我缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候,就可以直接从缓存中拿到,从而达到了减小数据库压力的目的。
但这种解决方式有两个缺点:(1)需要缓存层提供更多的内存空间来缓存这些空对象,当这种空对象很多的时候,就会浪费更多的内存;(2)会导致缓存层和存储层的数据不一致,即使在缓存空对象时给它设置了一个很短的过期时间,那也会导致这一段时间内的数据不一致问题。
2)使用布隆过滤器
简单地说就是在缓存前面加了一个过滤器,查询一个数据时布隆过滤器中存在才继续查询缓存,否则直接返回空值。
注意,布隆过滤器可能误判(不存在的肯定不存在,存在的也可能不存在)
布隆过滤器算法 参考文章
二、缓存击穿
1、含义
缓存击穿是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存(并发高),直接查询数据库。
2、解决方案
1)使用分布式锁,保证同一时刻只能有一个查询请求重新加载热点数据到缓存中,这样,其他的线程只需等待该线程运行完毕,即可重新从Redis中获取数据
2)在后台同时启一个定时任务去定时地更新这个缓存。
三、缓存雪崩
1、含义
缓存雪崩是指当缓存中有大量的key在同一时刻过期,导致大量的查询请求全部到达数据库,造成数据库查询压力骤增,甚至直接挂掉。
2、解决方案
给Redis设置过期时间时额外添加一个随机时间,来打散key的过期时间
如何解决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——缓存穿透缓存击穿缓存雪崩的主要内容,如果未能解决你的问题,请参考以下文章
REDIS12_缓存雪崩缓存穿透基于布隆过滤器解决缓存穿透的问题缓存击穿基于缓存击穿工作实际案例
REDIS12_缓存雪崩缓存穿透基于布隆过滤器解决缓存穿透的问题缓存击穿基于缓存击穿工作实际案例