學習之路十九:模仿ASP.NET的緩存依賴自定義緩存機制


項目的性能一直處於中下等的水平,也一直在摸索着,自從前幾周優化了WCF那一塊之后,速度明顯提高了,可是程序還不是理想中的要求,所以還要繼續努力!

一丶情境

前一段時間發現多個客戶端在第一次連接Server的時候會頻繁獲取同樣的數據,也就說每個客戶端獲取數據都要去數據庫查找一次,同一時刻多次的去數據庫獲取數據也是一件很費時間的事!

想來想去,決定把那些基本不變的數據緩存在Server,最后客戶端獲取數據的時候就不需要直接查找數據庫了!

上網查了一下關於設計緩存機制的資料,大部分都是ASP.NET Cache的文章!

雖然Winform也可以使用ASP.NET Cache,不過在我逐步研究的時候,發現它並不適合我們的項目,主要有下面幾個原因:

1. 需要把連接字符串配置到App.config中,而我們項目中的連接字符串是單獨放在一個指定的文件中的(這是阻礙我使用ASP.NET Cache的最大原因)

2. 有的時候需要使用命令來開始SQL Server緩存依賴通知(或者通過代碼來開啟),以及配置文件也需要一些修改

推薦文章:① 細說 ASP.NET Cache 及其高級用法

     ② PetShop之ASP.NET緩存

     ③ 並發環境下的緩存容器性能優化(上):不可變的哈希表

     ④ 系統緩存全解析6:數據庫緩存依賴

二丶學習

雖然不能把它應用到我們項目中,不過我還是繼續研究了一下它,並且在學習中我找到了一些自定義緩存的靈感!

當數據庫數據發生更新時,ASP.NET Cache為什么會自動刪除緩存數據呢,最重要的有兩點:

1. 當開啟緩存依賴的時候,會在你需要緩存的表中添加一個觸發器,然后會有一張表專門記錄緩存的表(由觸發器添加)最大更新時間

2. 會輪詢數據庫,也就是開啟一個Timer一直去監測數據庫

三丶思考

所以鑒於以上的想法,我決定自定義緩存,想法有三個方面:

1. 也是開啟一個Timer一直去監測你要緩存的數據表(每創建一個緩存依賴就會啟動一個Timer)

2. 定義一個HasChange屬性(學習ASP.NET Cache的),記錄數據庫表數據是否發生改變了

3. 我們數據庫中每一張表都有一個UpDT的字段,專門記錄最近更新的時間(我可以通過比較前后兩次時間來判斷數據是否發生了變化)

四丶實現

  1. 定義一個緩存基類(BaseCache),如下:

 1     public class BaseCache
 2     {
 3         private DateTime? _tableMaxUpdateTime = DateTime.Now; // 第一次記錄表中數據的最大更新時間
 4         private string _getMaxUpdateTimeSQL = string.Empty;
 5         private Timer _timer = new Timer(3000); // 開啟時鍾一直監測數據庫是否變化
 6         private bool _hasChange = false;
 7 
 8         protected BaseCache(string getMaxUpdateTimeSQL)
 9         {
10             _getMaxUpdateTimeSQL = getMaxUpdateTimeSQL;
11             _tableMaxUpdateTime = GetMaxUpdateTimeByTable(getMaxUpdateTimeSQL);  //第一次獲取最新時間
12             _timer.Elapsed += _timer_Elapsed;
13             _timer.Start();
14         }
15 
16         protected bool HasChange  //數據是否發生變化的標記
17         {
18             get { return _hasChange; }
19             set { _hasChange = value; }
20         }
21 
22         private void _timer_Elapsed(object sender, ElapsedEventArgs e)  //輪詢數據庫
23         {
24             try
25             {
26                 DateTime? latestMaxUpdateTime = GetMaxUpdateTimeByTable(_getMaxUpdateTimeSQL); //獲取最新的更新時間
27                 if (Convert.ToDateTime(_tableMaxUpdateTime).CompareTo(latestMaxUpdateTime) != 0)  //比較前后兩次的時間是否相同
28                 {
29                     _tableMaxUpdateTime = latestMaxUpdateTime;
30                     HasChange = true;
31                 }
32             }
33             catch (Exception exception)
34             {
35                 LoggingManager.WriteLog(LogLevel.Exception, exception.Message + Environment.NewLine + exception.StackTrace );
36             }
37         }
38 
39         private DateTime? GetMaxUpdateTimeByTable(string getMaxUpdateTimeSQL)
40         {
41             return new DAL.Cache().GetMaxUpdateTime(getMaxUpdateTimeSQL);
42         }
43 
44         public virtual void LoadCache() { }
45     }

   2. 定義具體的緩存依賴項 ProductClassCacheDependency,繼承BaseCache

 1     public class ProductClassCacheDependency : BaseCache
 2     {
 3         private static Dictionary<int, ProClassInfo> _productClassCache = new Dictionary<int, ProClassInfo>(150);  //通過定義靜態變量來作為緩存容器
 4         private static object _obj = new object();
 5 
 6         public ProductClassCacheDependency(string getMaxUpdateTimeSQL)
 7             : base(getMaxUpdateTimeSQL)
 8         {
 9             LoadCache();
10         }
11 
12         public override void LoadCache()
13         {
14             Dictionary<int, ProClassInfo> productCalssInfo = new ProClass().GetAllProductClassInfos();
15             if (productCalssInfo != null)
16             {
17                 _productClassCache.Clear();
18                 _productClassCache = productCalssInfo;
19             }
20         }
21 
22         public Dictionary<int, ProClassInfo> ProductClassCache
23         {
24             get { return UpdateCache(); }
25         }
26 
27         public Dictionary<int, ProClassInfo> UpdateCache()
28         {
29             if (HasChange) //采用雙判斷和鎖來實現同步機制
30             {
31                 lock (_obj) 
32                 {
33                     if (HasChange)
34                     {
35                         LoadCache();
36                         HasChange = false;
37                     }
38                 }
39             }
40             return _productClassCache;
41         }
42     }

   3. 定義緩存代理CacheProxy,調用緩存的入口點

 1     public class CacheProxy
 2     {
 3         private static CacheProxy _cacheProxy = null;
 4         private static object _obj = new object(); 6         private static ProductClassCacheDependency _productClassCache = null; 8 
 9         private CacheProxy() { }
10 
11         static CacheProxy() { }
12 
13         public static CacheProxy Cache
14         {
15             get
16             {
17                 if (_cacheProxy == null)
18                 {
19                     lock (_obj)
20                     {
21                         return _cacheProxy ?? (_cacheProxy = new CacheProxy());
22                     }
23                 }
24                 return _cacheProxy;
25             }
26         }
27 
33         public ProductClassCacheDependency ProductClassCache
34         {
35             get { return _productClassCache ?? (_productClassCache = new ProductClassCacheDependency("SELECT MAX(UpDT) FROM proDTProClass")); }  
42        //這邊可能處理的不好,沒有ASP.NET Cache直接傳一個表名好,需要改進
36 } 43 public void LoadAllCache() //程序啟動的時候就加載所有的緩存 44 { 46 _productClassCache = ProductClassCache; 47 } 48 }

   4. 調用方式

1  if(CacheProxy.Cache.ProductClassCache.ProductClassCache.ContainsKey(1) && CacheProxy.Cache.ProductClassCache.ProductClassCache != null)
  {
    CacheProxy.Cache.ProductClassCache.ProductClassCache[1]
  }

 

五丶總結

  經過了兩三周的辛苦努力終於結束了,其實這個過程也痛苦的(主要是不怎么會設計代碼,想的頭都大了),不過結果還是挺好的!

  如果覺得文章里哪里說的不好的,還請指出啊,我知道代碼還需要修改,大牛們可以多提提建議哦,一定會虛心接受的!

 

已同步至:程序猿個人文章目錄索引


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM