項目的性能一直處於中下等的水平,也一直在摸索着,自從前幾周優化了WCF那一塊之后,速度明顯提高了,可是程序還不是理想中的要求,所以還要繼續努力!
一丶情境
前一段時間發現多個客戶端在第一次連接Server的時候會頻繁獲取同樣的數據,也就說每個客戶端獲取數據都要去數據庫查找一次,同一時刻多次的去數據庫獲取數據也是一件很費時間的事!
想來想去,決定把那些基本不變的數據緩存在Server,最后客戶端獲取數據的時候就不需要直接查找數據庫了!
上網查了一下關於設計緩存機制的資料,大部分都是ASP.NET Cache的文章!
雖然Winform也可以使用ASP.NET Cache,不過在我逐步研究的時候,發現它並不適合我們的項目,主要有下面幾個原因:
1. 需要把連接字符串配置到App.config中,而我們項目中的連接字符串是單獨放在一個指定的文件中的(這是阻礙我使用ASP.NET Cache的最大原因)
2. 有的時候需要使用命令來開始SQL Server緩存依賴通知(或者通過代碼來開啟),以及配置文件也需要一些修改
推薦文章:① 細說 ASP.NET Cache 及其高級用法
二丶學習
雖然不能把它應用到我們項目中,不過我還是繼續研究了一下它,並且在學習中我找到了一些自定義緩存的靈感!
當數據庫數據發生更新時,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]
}
五丶總結
經過了兩三周的辛苦努力終於結束了,其實這個過程也痛苦的(主要是不怎么會設計代碼,想的頭都大了),不過結果還是挺好的!
如果覺得文章里哪里說的不好的,還請指出啊,我知道代碼還需要修改,大牛們可以多提提建議哦,一定會虛心接受的!
已同步至:程序猿個人文章目錄索引