標籤:style blog http color io os 使用 ar strong
1 業務需求
緩衝來自資料庫的資料,不用頻繁到資料庫中載入。
2 使用模型
添加一個類 MyCache,然後在裡面添加靜態屬性欄位:
public static DataTable FolderData { get { string key = "FolderData"; object o = HttpRuntime.Cache[key]; if (o == null) { //從DB中載入資料 o = LoadFromDB(); //添加到緩衝中(分鐘到期) HttpRuntime.Cache.Insert(key, o, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(30)); } return (DataTable)o; } }
邏輯是取資料時如果沒有,則去資料庫中載入,然後添加到緩衝中以便下次使用。
前台調用時直接用MyCache. FolderData即可。
3 啟動並執行問題
運行一段時間後,通過後台資料庫的監控,發現有一台Web伺服器還是有多次載入的SQL發送到資料庫的情況,而且發生的還很頻繁。為什麼緩衝沒有生效呢?丟了?添加監控日誌:
public static DataTable FolderData { get { string key = "FolderData"; object o = HttpRuntime.Cache[key]; if (o == null) { //從DB中載入資料 o = LoadFromDB(); saveLog("Add to Cache Key:" + key); //添加到緩衝中(分鐘到期)(指定回呼函數) HttpRuntime.Cache.Insert(key, o, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(30), CacheItemPriority.Normal, ASPCacheRemoveCallBack); } return (DataTable)o; } }
A 添加進入Cache時,記錄日誌。
B 添加刪除的回呼函數,當緩衝被刪除時,記錄日誌。
private static void ASPCacheRemoveCallBack (string key, object value, CacheItemRemovedReason reason) { saveLog("Key:" + key + "被刪除,原因:" + reason + ",時間:" + DateTime.Now.ToString()); }
然後分析這些日誌,發現緩衝有多次被刪除,刪除的原因是UnderUsed。
// 摘要:
// 之所以從緩衝中移除該項,是因為系統要通過移除該項來釋放記憶體。
Underused = 3,
但是連上伺服器看,實際記憶體還有很多。(系統一共16G,使用的才11.5G,而且w3wp.exe進程才佔用500MB。
為什麼還有很多記憶體剩餘,但是卻說記憶體不夠要把緩衝的資料清理掉呢?因為是緩衝,目的是少去資料庫,緩衝的東西如果多了,佔用記憶體大了,OS不是還有虛擬記憶體嗎?可以交換到硬碟呀,當然,如果這種情況發生,我們就把緩衝的到期時間設小一點,少緩衝點資料,維持系統平衡。但是不能還剩下4~5G的時候就不緩衝了呀。
添加可用的日誌:
private static void ASPCacheRemoveCallBack (string key, object value, CacheItemRemovedReason reason) { saveLog("Key:" + key + "被刪除,原因:" + reason + ",時間:" + DateTime.Now.ToString() + "可用數:" + HttpRuntime.Cache.EffectivePrivateBytesLimit); }
查看日誌,發現EffectivePrivateBytesLimit的值是0.
查閱資料:
http://msdn.microsoft.com/zh-SG/library/system.web.caching.cache.effectiveprivatebyteslimit(v=vs.80)
EffectivePrivateBytesLimit 屬性返回應用程式進程可使用的KB數。達到此限制後,緩衝演算法開始積極清理緩衝。
可使用應用程式設定檔中的 caching 的 cache 元素(ASP.NET 設定架構) 元素的 privateBytesLimit 屬性 (Attribute) 設定 EffectivePrivateBytesLimit 屬性 (Property)。未設定 privateBytesLimit 屬性時,緩衝演算法將確定緩衝的最大大小,EffectivePrivateBytesLimit 屬性將包含該演算法所選擇的大小。
為0表示ASP.net 認為沒有可分配的記憶體了嗎?實際情況並不是這樣,系統剛運行一個緩衝也沒有時,它也是輸出0。
進一步查看Web.Config,我們並沒有設定privateBytesLimit屬性的值。
IIS上設定了最大記憶體限制嗎?
也沒有設定記憶體的限制,而且也沒有設定要定時回收進程,這點可以從Application的變數沒有重新初始化丟失得到驗證。
同樣的程式部署運行在另外好幾套環境,EffectivePrivateBytesLimit的值也是0,但是它們沒有頻繁發生UnderUsed的情況。所以EffectivePrivateBytesLimit的值為0並不代表沒有記憶體可用了,需要清理,反而更像沒有對此屬性做設定的意思。
那到底是什麼原因引起的ASP.net 的清理記憶體呢?
繼續查閱資料:http://msdn.microsoft.com/zh-cn/library/ms228248%28v=vs.100%29.aspx
發現可以有幾個設定來控制緩衝的設定:
根據實際運行情況, 我們猜想:ASP.net 面臨記憶體壓力時好像算錯了,好吧,我們讓它不要算了,只要清理到期的資料就好了。把disableMemoryCollection設定為true,告訴ASP.net面臨記憶體壓力時不要回收記憶體。
連續運行3天,ASP.net 正常清理到期的資料,但是沒有再出現刪除原因為UnderUsed的日誌,記憶體佔用也穩定,沒有大的波動。
4 結語
本來還想繼續深入尋找ASP.net Cache的緩衝的清理記憶體機制到底是什麼,到底為什麼出問題,初步反射了下它的代碼,沒有準確找到,再說這個問題是 MS搞出來的,負責人應該是它,不應該我們幫它排錯(哈哈,偷懶的借口,不過每個人的精力是有限的,要解決回答為什麼的問題是無限的。。。)
本文最終的結論就是設定了一個配置值而已,這麼小的事似乎沒必要發Blog,但是發現大批提到UnderUsed和disableMemoryCollection的文章都只有1句話的解釋,沒有詳細的分析排錯的過程,故而獻醜寫一篇,如有不對,還請大家指出,謝謝!
ASP.net Cache丟失的問題診斷