1.缓存击穿
产生原因:如图,热点数据一般我们会在Redis存储一份缓存,避免请求直接到数据库给数据库层面一个很大压力,在这个缓存突然失效时,就会产生缓存失效,请求打到数据库的场景,这就是缓存击穿。
解决方案:
- 设置热点数据永远不过期。
- 加互斥锁(一般多个线程会重建缓存,锁住让第一个线程重新构建缓存,后面的线程就可以获取redis数据)简单实现:
String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, 3 * 60)
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他线程休息50毫秒后重试
Thread.sleep(50);
get(key);
}
}
}
2.缓存穿透
产生原因:存在一个ID数据库和redis中都不存在,每次请求过来时请求略过缓存必定会请求数据库。
解决方案:
1.接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
2.利用布隆过滤器过滤不存在于数据库中的key
3.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
3.缓存雪崩
产生原因:数据库中存在大批量的缓存在同一时间失效,并发请求过来数据库的压力会很大。
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同缓存数据库中。
- 设置热点数据永远不过期。(但是可能需要设置定时刷新缓存的功能,增加代码复杂度)