Redis中緩存雪崩、緩存穿透、緩存降級等概念的簡單說明(言簡意赅,不啰嗦):
1、緩存雪崩:緩存集中過期,新緩存還沒能刷入進來,導緻所有請求(查詢)都走資料庫,給資料庫記憶體和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是鎖着的,導緻使用者等待逾時。
另一個解決方法是:給每一個緩存資料增加相應的緩存标記,記錄緩存是否失效,如果緩存失效,從新刷入緩存。
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的緩存。
緩存資料:資料的過期時間是緩存标記的過期時間的2倍,當緩存标記key過期後,實際緩存還能把舊資料傳回給使用者,直到另外的線程在背景更新完緩存後,才傳回新緩存的資料。
設定緩存時候,盡量設定為一個随機的過期時間,盡量避免大量的緩存設定相同的過期時間,避免集中過期。
2、緩存穿透
這其實就是緩存命中率問題:使用者查找的資訊(例:id),在資料庫中不存在,緩存中更沒有。這就造成,每次都去資料庫查一次,然後傳回空。
解決辦法就是:如果查詢資料庫為空,直接設定一個預設的緩存值到緩存,這樣再通路這個資料的時候,直接傳回預設值,不會再通路資料庫了。對需要查詢的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;
}
}
3、緩存預熱
所謂預熱,就是在使用者使用之前,把需要緩存的資料,先緩存到緩存系統。
操作方法:
(1)、寫個刷緩存的程式,上線前,手動刷一下。
(2)、通路量低谷時定期定時刷緩存(淩晨3點-4點)。
4、緩存更新
(1)、通路量低谷時定期定時刷緩存淩晨3點-4點)。
(2)、當有使用者請求時,再去判斷是否過期,過期的話,再更新緩存。
5、緩存降級
(1)、緩存降級:核心就是棄車保帥,保證核心服務可用,降級可以丢棄的服務,可以讓程式實施自動降級,也可以人工緊急降級。
6、分布式緩存系統的問題
(1)、緩存系統與底層資料庫的一緻性。底層系統是可讀可寫時,很重要。
(2)、緩存分層時候,不同層緩存的一緻性。例如:全局緩存,二級緩存,全局緩存可以有二級緩存來組成。
(3)、多個緩存副本的一緻性。緩存系統背後往往是兩套緩存系統(如memcached、redis等)。
7、緩存淘汰:
(1)、定時定期去清理過期的緩存。
(2)、使用者請求時再去更新緩存。
8、緩存算法(緩存滿了時候,那種緩存先淘汰)
(1)、FIFO算法,先進先出。先緩存,先被淘汰。
(2)、LFU算法,最不經常使用算法。
(3)、LRU算法,近期最少使用算法。
備注: 根據部落格和《深入了解redis》一書了解和整理。