1、缓存穿透
1.1、问题描叙
当系统中引入redis
缓存后,一个请求进来后,会先从
redis
缓存中查询,缓存有就直接返回,缓存中没 有就去db
中查询,
db
中如果有就会将其丢到缓存中,但是有些
key
对应更多数据在
db
中并不存在,
每次针对此次key的请求从缓存中取不到,请求都会压到
db
,从而可能压垮
db
。
比如用一个不存在的用户
id
获取用户信息,不论缓存还是数据库都没有,若黑客利用大量此类攻击可能压垮数据库
![](https://img-blog.csdnimg.cn/direct/5f060046669a4aa1b30cd73df8094430.png)
1.2、解决方案
(1)对空值缓存
如果一个查询返回的数据为空(不管数据库是否存在),我们仍然把这个结果(null
)进行缓存,给其设置一个很短的过期时间,最长不超过五分钟
(
2
)设置可访问的名单(白名单)
使用redis
中的
bitmaps
类型定义一个可以访问的名单,名单
id
作为
bitmaps
的偏移量,每次范文和 bitmap里面的
id
进行比较,如果访问的
id
不在
bitmaps
里面,则进行拦截,不允许访问
(
3
)采用布隆过滤器
布隆过滤器(Bloom Filter
)是
1970
年有布隆提出的,它实际上是一个很长的二进制向量(位图和一系列随机映射函数(哈希函数)。
布隆过滤器可以用于检测一个元素是否在一个集合中,它的优点是空间效率和查询的世界都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
将所有可能存在的数据哈希到一个足够大的bitmaps
中,一个一定不存在的数据会被这个
bitmaps
拦截 掉,从而避免了对底层存储系统的查询压力。
(
4
)进行实时监控
当发现redis
的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制对其提供服务(比如:IP
黑名单)
2、缓存击穿
2.1、问题描述
redis中某个热点key(访问量很高的key)过期,此时大量请求同时过来,发现缓存中没有命中,这些请 求都打到db上了,导致db压力瞬时大增,可能会打垮db,这种情况成为缓存击穿。
缓存击穿出现的现象
- 数据库访问压力瞬时增大
- redis里面没有出现大量的key过期
- redis正常运行
2.2、解决方案
key可能会在某些时间点被超高并发地访问,是一种非常
“
热点
”
的数据,这个时候,要考虑一个问题:缓
存被
“
击穿
”
的问题,常见的解决方案如下
(
1
)预先设置热门数据,适时调整过期时间
在redis
高峰之前,把一些热门数据提前存入到
redis
里面,对缓存中的这些热门数据进行监控,实时调整过期时间。
(
2
)使用锁
缓存中拿不到数据的时候,此时不是立即去db
中查询,而是去获取分布式锁(比如
redis
中的
setnx
), 拿到锁再去db
中
load
数据;没有拿到锁的线程休眠一段时间再重试整个获取数据的方法。
3、缓存雪崩
3.1、问题描述
key对应的数据存在,但是极短时间内有大量的key集中过期,此时若有大量的并发请求过来,发现缓存 没有数据,大量的请求就会落到db上去加载数据,会将db击垮,导致服务奔溃。 缓存雪崩与缓存击穿的区别在于:前者是大量的key集中过期,而后者是某个热点key过期。
3.2、解决方案
(
1
)构建多级缓存
nginx缓存
+redis
缓存
+
其他缓存(
ehcache
等)
(
2
)使用锁或队列
用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上,不适用高并发情况。
(
3
)监控缓存过期,提前更新
监控缓存,发下缓存快过期了,提前对缓存进行更新。
(
4
)将缓存失效时间分散开
比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5
分钟随机,这样缓存的过期时间重复率就会降低,就很难引发集体失效的事件