Redis 缓存穿透 缓存击穿 缓存雪崩
缓存穿透
缓存穿透,字面理解,就是缓存形同虚设,请求直接穿过去了。
当查询缓存和数据库中都没有的数据时,就会产生这种情况。
举个栗子:如发起为id为“-1”的数据查询时,缓存和数据库都查不到结果,这时的用户很可能是攻击者,频繁攻击会导致数据库压力过大,直接挂掉。
那么该怎么解决呢?
-
接口层增加校验
如用户鉴权校验,id做基础校验,id<=0的直接拦截。 -
缓存空数据,将数据库返回的null值进行缓存。
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。 -
采用布隆过滤器进行拦截。
将数据库中所有的查询条件,放入布隆过滤器中,
当一个查询请求过来时,先经过布隆过滤器进行查,如果判断请求查询值存在,则继续查;如果判断请求查询不存在,直接丢弃。
布隆过滤器显示结果存在,有一定概率它是不存在的,但是如果布隆过滤器的结果显示不存在,那么它一定不存在。
缓存击穿
缓存击穿就是在大量请求的情况下,某一个缓存数据突然失效(就好像是大量的请求把这个缓存数据给击穿了),导致大量的请求直接怼到DB上,把DB搞死了。
解决方案:
- 设置key永不过期
- 加互斥锁
//伪代码
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
缓存雪崩
缓存雪崩和缓存击穿的区别就是缓存雪崩是大面积的key失效。
解决方案:
- 设置key永不过期
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。