項目的效能一直處於中下等的水平,也一直在摸索著,自從前幾周最佳化了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 try25 {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> ProductClassCache23 {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 Cache14 {15 get16 {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 ProductClassCache34 {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]
}
五丶總結
經過了兩三周的辛苦努力終於結束了,其實這個過程也痛苦的(主要是不怎麼會設計代碼,想的頭都大了),不過結果還是挺好的!
如果覺得文章裡哪裡說的不好的,還請指出啊,我知道代碼還需要修改,大牛們可以多提提建議哦,一定會虛心接受的!
已同步至:程式猿個人文章目錄索引