缓存穿透

当客户端频繁地向服务器请求一个缓存中不存在的数据时,服务器会不断查询数据库,数据库可能会因此挂掉。

解决方案

这里有一个简单粗暴的方法。

当查询数据库时,如果结果返回null,这时仍然对其结果进行缓存,但缓存的过期时间很短。

缓存雪崩

在设置缓存时都设置了相同的过期时间,致使缓存同时失效,所有请求将全部转发给数据库,数据库瞬时压力过重,导致雪崩。

解决方案

可以在原有的失效时间基础上增加一个随机值,使得每个key的过期时间分散开,不会集中在同一时间段失效。

缓存击穿

一个存在的key,在缓存过期的那一刻,刚好有大量的请求,大并发的请求会击穿到DB,造成瞬时DB请求量大,压力骤增。
这里的缓存击穿和缓存雪崩看似相同,实际上有一点区别。

  • 击穿是针对某一个 key
  • 雪崩是有很多的 key 同时失效

解决方案

在访问 key 之前,可以采用 SETNX(set if not exists)来设置另一个短期的 key,通过其来锁住当前 key 的访问,访问结束就删除该短期 key。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}

评论