1. 程式人生 > >redis中的緩存-緩存雪崩和緩存穿透

redis中的緩存-緩存雪崩和緩存穿透

https public product http sig emp read 清理 cache

緩存雪崩

  緩存雪崩是由於原有緩存失效(過期),新緩存未到期間。所有請求都去查詢數據庫,而對數據庫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中的緩存-緩存雪崩和緩存穿透