1.緩存粒度控制
通俗來講,緩存粒度問題就是我們在使用緩存時,是將所有數據緩存還是緩存部分數據?
緩存粒度問題是一個容易被忽視的問題,如果使用不當,可能會造成很多無用空間的浪費,可能會造成網絡帶寬的浪費,可能會造成代碼通用性較差等情況,必須學會綜合數據通用性、空間佔用比、代碼維護性 三點評估取捨因素權衡使用。
2.緩存穿透問題
緩存穿透是指查詢一個一定不存在的數據,由於緩存不命中,並且出於容錯考慮, 如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。
可能造成原因:
1.業務代碼自身問題 2.惡意攻擊。爬蟲等等
危害
對底層數據源壓力過大,有些底層數據源不具備高併發性。 例如mysql一般來說單臺能夠扛1000-QPS就已經很不錯了
解決方案
1.緩存空對象
```java
public class NullValueResultDO implements Serializable{
private static final long serialVersionUID = -6550539547145486005L;
}
public class UserManager {
UserDAO userDAO;
LocalCache localCache;
public UserDO getUser(String userNick) {
Object object = localCache.get(userNick);
if(object != null) {
if(object instanceof NullValueResultDO) {
return null;
}
return (UserDO)object;
} else {
User user = userDAO.getUser(userNick);
if(user != null) {
localCache.put(userNick,user);
} else {
localCache.put(userNick, new NullValueResultDO());
}
return user;
}
}
}
```
2.布隆過濾器
3.緩存擊穿.熱點key重建緩存問題
緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由於併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
我們知道,使用緩存,如果獲取不到,纔會去數據庫裏獲取。但是如果是熱點 key,訪問量非常的大,數據庫在重建緩存的時候,會出現很多線程同時重建的情況。因爲高併發導致的大量熱點的 key 在重建還沒完成的時候,不斷被重建緩存的過程,由於大量線程都去做重建緩存工作,導致服務器拖慢的情況。
解決方案
1.互斥鎖
第一次獲取緩存的時候,加一個鎖,然後查詢數據庫,接着是重建緩存。這個時候,另外一個請求又過來獲取緩存,發現有個鎖,這個時候就去等待,之後都是一次等待的過程,直到重建完成以後,鎖解除後再次獲取緩存命中。
```java
public String getKey(String key){
String value = redis.get(key);
if(value == null){
String mutexKey = "mutex:key:"+key; //設置互斥鎖的key
if(redis.set(mutexKey,"1","ex 180","nx")){ //給這個key上一把鎖,ex表示只有一個線程能執行,過期時間爲180秒
value = db.get(key);
redis.set(key,value);
redis.delete(mutexKety);
}else{
// 其他的線程休息100毫秒後重試
Thread.sleep(100);
getKey(key);
}
}
return value;
}
```
互斥鎖的優點是思路非常簡單,具有一致性,但是互斥鎖也有一定的問題,就是大量線程在等待的問題。存在死鎖的可能性。
4.緩存雪崩問題
緩存雪崩是指機器宕機或在我們設置緩存時採用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩。
1:在緩存失效後,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。
2:做二級緩存,A1爲原始緩存,A2爲拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置爲短期,A2設置爲長期
3:不同的key,設置不同的過期時間,讓緩存失效的時間點儘量均勻。
4:如果緩存數據庫是分佈式部署,將熱點數據均勻分佈在不同的緩存數據庫中。