編程技術緩衝寫法(一)

來源:互聯網
上載者:User

介紹

本篇主要說下平常項目中緩衝使用經驗和遇到過的問題。

目錄

一: 基本寫法

二:緩衝雪崩

1:全域鎖,執行個體鎖

2:字串鎖

三:緩衝穿透

四:再談緩衝雪崩

五:總結

一:基本寫法

為了方便示範,我們用Runtime.Cache做緩衝容器,並定義個簡單操作類。如下:

public class CacheHelper   {       public static object Get(string cacheKey)       {           return HttpRuntime.Cache[cacheKey];       }       public static void Add(string cacheKey, object obj, int cacheMinute)       {           HttpRuntime.Cache.Insert(cacheKey, obj, null, DateTime.Now.AddMinutes(cacheMinute),               Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);       }   }

簡單讀取:

public object GetMemberSigninDays1()    {        const int cacheTime = 5;        const string cacheKey = "mushroomsir";         var cacheValue = CacheHelper.Get(cacheKey);        if (cacheValue != null)            return cacheValue;         cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數        CacheHelper.Add(cacheKey, cacheValue, cacheTime);        return cacheValue;    }

在項目中,有不少這樣寫法。這樣寫沒有錯,但在並發量上來後就會有問題。繼續看

二:緩衝雪崩

緩衝雪崩是由於緩衝失效(到期),新緩衝未到期間。

這個中間時間內,所有請求都去查詢資料庫,而對資料庫CPU和記憶體造成巨大壓力,前端串連數不夠、查詢阻塞。

這個中間時間並沒有那麼短,比如sql查詢1秒,加上傳輸解析0.5秒。 就是說1.5秒內所有使用者查詢,都是直接查詢資料庫的。

這種情況下,我們想到最多的就是加鎖排隊了。

1:全域鎖,執行個體鎖

public static object obj1 = new object();       public object GetMemberSigninDays2()       {           const int cacheTime = 5;           const string cacheKey = "mushroomsir";            var cacheValue = CacheHelper.Get(cacheKey);            if (cacheValue != null)               return cacheValue;            //lock (obj1)         //全域鎖           //{           //    cacheValue = CacheHelper.Get(cacheKey);           //    if (cacheValue != null)           //        return cacheValue;           //    cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數           //    CacheHelper.Add(cacheKey, cacheValue, cacheTime);           //}           lock (this)           {               cacheValue = CacheHelper.Get(cacheKey);               if (cacheValue != null)                   return cacheValue;                cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數               CacheHelper.Add(cacheKey, cacheValue, cacheTime);           }           return cacheValue;       }

第一種:lock (obj1) 是全域鎖可以滿足,但我們要為每個函數都聲明一個obj,不然在A、B函數都鎖obj1時,必然會讓其中一個阻塞。

第二種:lock (this) 這個鎖當前執行個體,對其他執行個體無效,這個鎖就沒什麼效果了。使用單例模式的可以鎖。

但在當前執行個體中:A函數鎖當前執行個體,其他鎖當前執行個體的函數讀寫,也被阻塞。 不可取

2:字串鎖

既然鎖對象不行,利用字串的特性,我們直接鎖緩衝key呢。來看下

public object GetMemberSigninDays3()       {           const int cacheTime = 5;           const string cacheKey = "mushroomsir";            var cacheValue = CacheHelper.Get(cacheKey);           if (cacheValue != null)               return cacheValue;           const string lockKey = cacheKey + "n(*≧▽≦*)n";            //lock (cacheKey)           //{           //    cacheValue = CacheHelper.Get(cacheKey);           //    if (cacheValue != null)           //        return cacheValue;           //    cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數           //    CacheHelper.Add(cacheKey, cacheValue, cacheTime);           //}           lock (lockKey)           {               cacheValue = CacheHelper.Get(cacheKey);               if (cacheValue != null)                   return cacheValue;               cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數               CacheHelper.Add(cacheKey, cacheValue, cacheTime);           }           return cacheValue;       }

第一種:lock (cacheName) 有問題,因為字串也是共用的,會阻塞其他使用這個字串的操作行為。 具體請看之前的博文 c#語言-多線程中的鎖系統(一)。

2015-01-04 13:36更新:因為字串被公用語言運行庫 (CLR)暫留,這意味著整個程式中任何給定字串都只有一個執行個體。所以才會用第二種

第二種:lock (lockKey) 可以滿足。其實目就是為了保證鎖的粒度最小並且全域唯一性,只鎖當前緩衝的查詢行為。

三:緩衝穿透

舉個簡單例子:一般我們會緩衝使用者搜尋結果。而資料庫查詢不到,是不會做緩衝的。但如果頻繁查這個關鍵字,就會每次都直查資料庫了。

這樣緩衝就沒意義了,這也是常提的快取命中率問題。

public object GetMemberSigninDays4()      {          const int cacheTime = 5;          const string cacheKey = "mushroomsir";           var cacheValue = CacheHelper.Get(cacheKey);          if (cacheValue != null)              return cacheValue;          const string lockKey = cacheKey + "n(*≧▽≦*)n";           lock (lockKey)          {              cacheValue = CacheHelper.Get(cacheKey);              if (cacheValue != null)                  return cacheValue;               cacheValue = null; //資料庫查詢不到,為空白。              //if (cacheValue2 == null)              //{              //    return null;  //一般為空白,不做緩衝              //}              if (cacheValue == null)              {                  cacheValue = string.Empty; //如果發現為空白,我設定個預設值,也緩衝起來。              }              CacheHelper.Add(cacheKey, cacheValue, cacheTime);          }          return cacheValue;      }

例子中我們把查詢不到的結果,也給緩衝起來了。這樣就可以避免,查詢為空白時,引起緩衝穿透了。

當然我們也可以單獨設定個緩衝區,進行第一層控制校正。 以便和正常緩衝區分開了。

四:再談緩衝雪崩

額 不是用加鎖排隊方式就解決了嗎?其實加鎖排隊只是為了減輕DB壓力,並沒有提高系統輸送量。

在高並發下: 緩衝重建期間,你是鎖著的,1000個請求999個都在阻塞的。 使用者體驗不好,還浪費資源:阻塞的線程本可以處理後續請求的。

public object GetMemberSigninDays5()        {            const int cacheTime = 5;            const string cacheKey = "mushroomsir";             //快取標籤。            const string cacheSign = cacheKey + "_Sign";            var sign = CacheHelper.Get(cacheSign);             //擷取緩衝值            var cacheValue = CacheHelper.Get(cacheKey);            if (sign != null)                return cacheValue; //未到期,直接返回。             lock (cacheSign)            {                sign = CacheHelper.Get(cacheSign);                if (sign != null)                    return cacheValue;                 CacheHelper.Add(cacheSign, "1", cacheTime);                ThreadPool.QueueUserWorkItem((arg) =>                {                    cacheValue = "395"; //這裡一般是 sql查詢資料。 例:395 簽到天數                    CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期設緩衝時間的2倍,用於髒讀。                });            }            return cacheValue;        }

代碼中,我們多用個快取標籤key,雙檢鎖校正。它設定為正常時間,到期後通知另外的線程去更新快取資料。

而實際的緩衝由於設定了2倍的時間,仍然可以能用髒資料給前端展現。

這樣就能提高不少系統輸送量了。

五:總結

補充下: 這裡說的阻塞其他函數指的是,高並發下鎖同一對象。

實際使用中,緩衝層封裝往往要複雜的多。 關於更新緩衝,可以單開一個線程去專門跑這些,圖方便就扔線程池吧。

具體使用情境,可根據實際使用者量來平衡。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.