緩存擊穿和緩存穿透
1、緩存穿透
緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷發起請求,如發起為id為“-1”的數據或id為特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
解決方案:
- 接口層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截;
- 從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反復用同一個id暴力攻擊
2、緩存擊穿
緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由於並發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
解決方案:
- 設置熱點數據永遠不過期
- 加鎖
public User findUserById(Integer id) {
User user = null;
String key = CACHE_KEY_USER + id;
// 1、先從 redis 中查詢,如果有結果,直接返回,如果沒有,再去查詢 mysql
user = (User) redisTemplate.opsForValue().get(key);
if (user == null) {
// 2、進來就先加鎖,保證一個請求,讓外面的 redis 等待一下,避免擊穿mysql
synchronized (UserServiceImpl.class) {
user = (User) redisTemplate.opsForValue().get(key);
// 3、 再次查詢redis 還是 null, 就可以去查詢 mysql 了(mysql默認有數據)
if (user == null) {
// 5、redis 里面沒有,查詢Mysql
user = userMapper.selectByPrimaryKey(id);
if (user == null) {
// 6.1、redis + mysql 都沒有數據
return user;
} else {
// 6.2 、mysql中有數據 ,需要將數據寫回redis,保證下一次的緩存命中率
redisTemplate.opsForValue().setIfAbsent(key, user, 7, TimeUnit.DAYS);
}
}
}
}
return user;
}