redis中的緩存-緩存雪崩和緩存穿透
緩存雪崩
緩存雪崩是由於原有緩存失效(過期),新緩存未到期間。所有請求都去查詢數據庫,而對數據庫CPU和內存造成巨大壓力,嚴重的會造成數據庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。
1. 碰到這種情況,一般並發量不是特別多的時候,使用最多的解決方案是加鎖排隊。
public object GetProductListNew() { const int cacheTime = 30; const string cacheKey = "product_list"; const string lockKey = cacheKey; var cacheValue = CacheHelper.Get(cacheKey); if (cacheValue != null) { return cacheValue; } else { lock (lockKey) { cacheValue = CacheHelper.Get(cacheKey); if (cacheValue != null) { return cacheValue; } else { cacheValue = GetProductListFromDB(); //這裏一般是 sql查詢數據。 CacheHelper.Add(cacheKey, cacheValue, cacheTime); } } return cacheValue; } }
2. 加鎖排隊只是為了減輕數據庫的壓力,並沒有提高系統吞吐量。假設在高並發下,緩存重建期間key是鎖著的,這是過來1000個請求999個都在阻塞的。同樣會導致用戶等待超時,這是個治標不治本的方法。
還有一個解決辦法解決方案是:給每一個緩存數據增加相應的緩存標記,記錄緩存的是否失效,如果緩存標記失效,則更新數據緩存。
public object GetProductListNew() { const int cacheTime = 30; const string cacheKey = "product_list"; //緩存標記。 const string cacheSign = cacheKey + "_sign"; var sign = CacheHelper.Get(cacheSign); //獲取緩存值 var cacheValue = CacheHelper.Get(cacheKey); if (sign != null) { return cacheValue; //未過期,直接返回。 } else { CacheHelper.Add(cacheSign, "1", cacheTime); ThreadPool.QueueUserWorkItem((arg) => { cacheValue = GetProductListFromDB(); //這裏一般是 sql查詢數據。 CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期設緩存時間的2倍,用於臟讀。 }); return cacheValue; } }
緩存標記:記錄緩存數據是否過期,如果過期會觸發通知另外的線程在後臺去更新實際key的緩存。
緩存數據:它的過期時間比緩存標記的時間延長1倍,例:標記緩存時間30分鐘,數據緩存設置為60分鐘。 這樣,當緩存標記key過期後,實際緩存還能把舊數據返回給調用端,直到另外的線程在後臺更新完成後,才會返回新緩存。
這樣做後,就可以一定程度上提高系統吞吐量。
緩存穿透
緩存穿透是指用戶查詢數據,在數據庫沒有,自然在緩存中也不會有。這樣就導致用戶查詢的時候,在緩存中找不到,每次都要去數據庫再查詢一遍,然後返回空。這樣請求就繞過緩存直接查數據庫,這也是經常提的緩存命中率問題。
解決的辦法就是:如果查詢數據庫也為空,直接設置一個默認值存放到緩存,這樣第二次到緩沖中獲取就有值了,而不會繼續訪問數據庫,這種辦法最簡單粗暴。
public object GetProductListNew() { const int cacheTime = 30; const string cacheKey = "product_list"; var 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進行預先校驗,然後再放行給後面的正常緩存處理邏輯。
緩存預熱
緩存預熱就是系統上線後,將相關的緩存數據直接加載到緩存系統。這樣避免,用戶請求的時候,再去加載相關的數據。
解決思路:
1,直接寫個緩存刷新頁面,上線時手工操作下。
2,數據量不大,可以在WEB系統啟動的時候加載。
3,定時刷新緩存,
緩存更新
緩存的清理機制可以參考文章:https://www.cnblogs.com/ricklz/p/10742560.html
redis中的緩存-緩存雪崩和緩存穿透