一、緩存處理流程
前台請求,后台先從緩存中取數據,取到直接返回結果,取不到時從數據庫中取,數據庫取到更新緩存,並返回結果,數據庫也沒取到,那直接返回空結果。
二、緩存穿透
描述:
緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷發起請求,如發起為id為“-1”的數據或id為特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
解決方案:
1、采用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。
2、從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反復用同一個id暴力攻擊
三、緩存擊穿
描述:
緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由於並發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
解決方案:
1、設置熱點數據永遠不過期。
2、加互斥鎖,互斥鎖參考代碼如下:
public static String getData(String key) { //從緩存里面讀取數據 String result= getDataFromRedis(key); //緩存中如果不存在 if(result == null) { //去獲取鎖,獲取成功,去數據庫取數據 if(reenLock.tryLock()) { //從數據庫獲取數據 result=getDataFromMyDB(key); //獲取到數據更新緩存數據 if(result != null) { setDataToCache(key,result); } //釋放鎖 reenLock.unlock(); } //獲取鎖失敗 else { //暫停100ms再去重新獲取數據 Thread.slepp(100); result =getDataFromRedis(key); } } return result; }
說明:
1)緩存中有數據,直接走上述代碼getDataFromRedis(key);就返回結果了
2)緩存中沒有數據,第1個進入的線程,獲取鎖並從數據庫去取數據,沒釋放鎖之前,其他並行進入的線程會等待100ms,再重新去緩存取數據。這樣就防止都去數據庫重復取數據,重復往緩存中更新數據情況出現。
3)當然這是簡化處理,理論上如果能根據key值加鎖就更好了,就是線程A從數據庫取key1的數據並不妨礙線程B取key2的數據,上面代碼明顯做不到這點。
四、緩存雪崩
描述:
緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是, 緩存擊穿指並發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。
解決方案:
1、加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的並發請求落到底層存儲系統上
2、緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
3、如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同搞得緩存數據庫中。
4、設置熱點數據永遠不過期。
其中緩存擊穿和雪崩提及到的解決方案均有:永不過期,這里包含兩層意思
(1) 從redis上看,確實沒有設置過期時間,這就保證了,不會出現熱點key過期問題,也就是“物理”不過期。
(2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value里,如果發現要過期了,通過一個后台的異步線程進行緩存的構建,也就是“邏輯”過期
String get(final String key) { var v = redis.get(key); String value = v.getValue(); long timeout = v.getTimeout(); if (v.timeout <= System.currentTimeMillis()) { // 異步更新后台異常執行 threadPool.execute(new Runnable() { public void run() { String keyMutex = "mutex:" + key; if (redis.setnx(keyMutex, "1")) { // 3 min timeout to avoid mutex holder crash redis.expire(keyMutex, 3 * 60); String dbValue = db.get(key); redis.set(key, dbValue); redis.delete(keyMutex); } } }); } return value; }