缓存穿透
概念
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉
解决方案
- 采用布隆过滤器,使用一个足够大的bitmap,用于用于存储可能访问的key,不存在的key直接被过滤掉
- 访问key未在DB查询到值时,也将空值写进缓存,但可以设置较短过期时间
//将空值写进缓存
public object GetProductListNew() {
int cacheTime = 30;
String cacheKey = "product_list";
String cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
return cacheValue;
}
cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
return cacheValue;
} else {
//数据库查询不到,为空
cacheValue = GetProductListFromDB();
if (cacheValue == null) {
//如果发现为空,设置个默认值,也缓存起来
cacheValue = string.Empty;
}
CacheHelper.Add(cacheKey, cacheValue, cacheTime);
return cacheValue;
}
}
缓存雪崩
概念
大量的key设置了相同的过期时间,导致缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增、造成雪崩。
解决方案
可以给缓存设置过期时间加上一个随机值,使得每个可以的过期时间分开来,不会集中在同意时刻失效
缓存击穿
概念
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求会击穿到DB,造成瞬间DB请求量大、压力骤增。
解决方案
在访问key之前,采用SETNT(set if not exist)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期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;
}
}