摘要: 介紹緩存的基本概念和常用的緩存技術(shù),給出了各種技術(shù)的實(shí)現機制的簡(jiǎn)單介紹和適用范圍說(shuō)明,以及設計緩存方案應該考慮的問(wèn)題(共17頁(yè))
· 性能——將相應數據存儲起來(lái)以避免數據的重復創(chuàng )建、處理和傳輸,可有效提高性能。比如將不改變的數據緩存起來(lái),例如國家列表等,這樣能明顯提高web程序的反應速度;
· 穩定性——同一個(gè)應用中,對同一數據、邏輯功能和用戶(hù)界面的多次請求時(shí)經(jīng)常發(fā)生的。當用戶(hù)基數很大時(shí),如果每次請求都進(jìn)行處理,消耗的資源是很大的浪費,也同時(shí)造成系統的不穩定。例如,web應用中,對一些靜態(tài)頁(yè)面的呈現內容進(jìn)行緩存能有效的節省資源,提高穩定性。而緩存數據也能降低對數據庫的訪(fǎng)問(wèn)次數,降低數據庫的負擔和提高數據庫的服務(wù)能力;
· 可用性——有時(shí),提供數據信息的服務(wù)可能會(huì )意外停止,如果使用了緩存技術(shù),可以在一定時(shí)間內仍正常提供對最終用戶(hù)的支持,提高了系統的可用性。
在深入介紹緩存技術(shù)之前,需要對狀態(tài)有一個(gè)認識,因為緩存可以說(shuō)是狀態(tài)管理的框架。理解狀態(tài)的含義和它的一些特性——比如生存期和生存范圍——對決定是否緩存和選擇合適的緩存技術(shù)有很大幫助。狀態(tài)是指一些數據,在應用系統某個(gè)時(shí)間點(diǎn)上,數據的狀態(tài)和條件。這些數據可能是永久性的存儲在數據庫中,可能是只在內存里停留一會(huì ),也可能是按照某個(gè)邏輯存活(比如多長(cháng)時(shí)間后釋放),它的應用范圍可能是所有用戶(hù)可訪(fǎng)問(wèn),可能是單個(gè)用戶(hù)有權限;
生存期是指數據保持有效性的時(shí)間區間,也就是從創(chuàng )建到移除的時(shí)間間隔。通常的生存期有以下幾種:
·永久狀態(tài)Permanent State——應用程序使用的永久數據;
·進(jìn)程狀態(tài)Process State——只在進(jìn)程周期內有效;
·會(huì )話(huà)狀態(tài)Session State——和特定的用戶(hù)會(huì )話(huà)有關(guān);
·消息狀態(tài)Message State——處理某個(gè)消息的時(shí)間內有效;
狀態(tài)的范圍指對該狀態(tài)有訪(fǎng)問(wèn)權限的物理或邏輯范圍。
·物理范圍指可以被訪(fǎng)問(wèn)到的狀態(tài)數據存放的物理位置,通常包括:
1、 組織Organization——在一個(gè)組織內的所有應用程序可以訪(fǎng)問(wèn)狀態(tài)數據;
2、 場(chǎng)Farm——在應用場(chǎng)范圍內的任何機器上都可以訪(fǎng)問(wèn);
3、 機器Machine——單個(gè)機器范圍內可以訪(fǎng)問(wèn);
4、 進(jìn)程Process——進(jìn)程內的訪(fǎng)問(wèn)許可;
5、 應用域AppDomain——應用程序域內的訪(fǎng)問(wèn)許可。
·邏輯范圍指可訪(fǎng)問(wèn)狀態(tài)數據的邏輯范圍,常見(jiàn)的有:
1、 應用程序Application;
2、 業(yè)務(wù)流程Business Process;
3、 角色Role;
4、 用戶(hù)User;
緩存的狀態(tài)數據只是主數據(Master State Data)的快照,由于數據源可能被修改,所以狀態(tài)數據就有會(huì )陳舊的特性。合理利用此特性和將數據陳舊的負面影響最小化是緩存狀態(tài)數據的一個(gè)重要任務(wù)。你可以以一下方式定義數據的陳舊依據:
·主數據更改的可能性——隨著(zhù)時(shí)間的推進(jìn),主數據更改的可能是否大大增加?安照這一點(diǎn)來(lái)決定緩存狀態(tài)數據的陳舊;
·更改的相關(guān)性——主數據更新時(shí),緩存的狀態(tài)數據不相應更新是不是造成影響系統的使用?比如,更改系統的外觀(guān)風(fēng)格并不會(huì )對業(yè)務(wù)造成很大影響。
緩存狀態(tài)數據的陳舊對業(yè)務(wù)流程的影響稱(chēng)為容忍度,應用系統的可以為不能容忍(No Tolerance)和一定程度的容忍(some Tolerance),前者必須和主數據同步更新,后者允許一定時(shí)間或一定范圍的陳舊,判斷標準就是對業(yè)務(wù)流程的影響度。
狀態(tài)的另一個(gè)屬性是在不同階段的表現形式。在數據庫中存儲的是原始格式的數據,業(yè)務(wù)流程中的是處理過(guò)的數據,給最終用戶(hù)呈現的則是另外的形式。如下表所示:
表現形式 | 描述 | 舉例 |
原始數據 | 數據的原始形式 | 如數據庫中的數據 |
處理過(guò)的數據 | 業(yè)務(wù)流程中對原始數據加工后的數據 | 業(yè)務(wù)過(guò)程中的數據形式 |
呈現形式 | 可呈現給最終用戶(hù)的形式 | HTML或可理解的文字說(shuō)明 |
當決定緩存數據時(shí),應該考慮緩存哪個(gè)階段(哪種形式)的狀態(tài)數據。以下方針有助于你做決定:
· 當業(yè)務(wù)邏輯可以容忍緩存數據的陳舊時(shí)就緩存原始數據;原始數據可以緩存在數據庫訪(fǎng)問(wèn)組件和服務(wù)代理中;
·緩存處理過(guò)的數據以減少處理時(shí)間和資源;處理過(guò)的數據可以緩存在業(yè)務(wù)邏輯組件和服務(wù)接口中。
·當需要呈現的數據量很大并且控件的呈現時(shí)間很長(cháng)時(shí),緩存呈現數據(比如包含大數據量的Treeview控件)。這種數據應該被緩存在UI控件中。
在應用程序中緩存數據有以下好處:
·減少交互的通訊量——緩存數據能有效減少在進(jìn)程和機器間的傳輸量;
·降低系統中的處理量——減少處理次數;
·降低需要做的磁盤(pán)訪(fǎng)問(wèn)次數——比如緩存在內存中的數據。
緩存數據只是一份主數據的拷貝,它可能在內存中或以不同的表現形式保存在硬盤(pán)上,也就是說(shuō),離說(shuō)句的使用者越近越好。所以,除了考慮要緩存哪些數據以外,數據緩存在哪里也是一個(gè)主要的考量點(diǎn)。這個(gè)問(wèn)題分為以下兩個(gè)范圍:
1、 存儲類(lèi)型Storage Type——數據可用的物理存儲位置;
2、 層間的架構元素(Layered architecture elements)——數據可用的邏輯存儲位置。
緩存有很多實(shí)現方法,所有這些可以被分為兩類(lèi),基于內存的緩存和基于磁盤(pán)的緩存:
1、 內存駐留緩存——包含在內存中臨時(shí)存儲數據的所有實(shí)現方法,通常在以下情況下使用:
a) 應用程序頻繁使用同樣的數據;
b) 應用程序需要經(jīng)常獲取數據;
通過(guò)將數據保留在內存中,你可以有效降低昂貴的磁盤(pán)訪(fǎng)問(wèn)操作,也可以通過(guò)將數據保留在使用者進(jìn)程中來(lái)最大程度的減少跨進(jìn)程的數據傳輸。
2、 磁盤(pán)駐留緩存——這種技術(shù)包含所有使用磁盤(pán)作為存儲介質(zhì)的緩存技術(shù),如文件和數據庫。在以下情況下基于磁盤(pán)的緩存是很有效的:
a) 處理大數據量時(shí);
b) 應用服務(wù)提供的數據可能并不是總能使用(比如離線(xiàn)的情況);
c) 緩存的數據必須能在進(jìn)程回收和機器重啟的情況下保持有效;
通過(guò)緩存處理過(guò)的數據,你可以有效降低數據處理的負擔,同時(shí)可減少數據交互的代價(jià)。
應用程序中的每個(gè)邏輯層的組件都會(huì )處理數據,下圖標識了一些通用組件:

當使用這些組件進(jìn)行工作時(shí),你需要考慮哪些數據可以被緩存起來(lái),還有以哪種方式進(jìn)行緩存會(huì )對程序的整體性能和可用性有幫助,以上的這些元素都可以緩存相應的數據。當然,要考慮的遠不止這些。
當設計一個(gè)緩存方案時(shí),不但要考慮緩存哪些數據、數據緩存到哪里,還有其它的因素需要考慮。
當決定是否緩存一個(gè)對象時(shí),關(guān)于數據的格式和訪(fǎng)問(wèn)機制,你需要考慮三個(gè)主要問(wèn)題:
1、 線(xiàn)程安全——當緩存的內容可以被多個(gè)線(xiàn)程訪(fǎng)問(wèn)時(shí),使用某種鎖定機制來(lái)保證數據不會(huì )被兩個(gè)線(xiàn)程同時(shí)操作;
2、 序列化——將一個(gè)對象緩存時(shí),需要將它序列化以便保存,所以包緩存的對象必須支持序列化;
3、 規格化緩存數據——緩存數據時(shí),相對于要使用的數據格式而言,要保證數據的格式是優(yōu)化過(guò)的。
在使用緩存數據前,必須將數據加載到緩存中,有兩種機制來(lái)加載數據:
·提前加載Proactive Load——使用這種方式時(shí),你提前將所有的狀態(tài)數據加載到緩存中,可能在應用程序或線(xiàn)程啟動(dòng)時(shí)進(jìn)行,然后在應用程序或線(xiàn)程的生存期內一直緩存;
·動(dòng)態(tài)加載Reactive Load——或稱(chēng)反應式加載,當使用這種方法時(shí),在應用程序請求數據時(shí)取到數據,并且將它緩存起來(lái)以備后續使用。
另外一個(gè)關(guān)鍵因素是如何保持緩存數據和主數據(文件或數據庫或其他的應用程序資源)的一致性,你可以定義過(guò)期策略來(lái)決定緩存中的內容,如已經(jīng)緩存的時(shí)間或者收到其他資源的通知。
當緩存數據時(shí),需要非常清楚緩存中數據的潛在安全威脅。緩存中的數據可能會(huì )被別的進(jìn)程訪(fǎng)問(wèn)或修改,而此進(jìn)程對主數據是沒(méi)有權限的。原因是當數據存儲在原始位置時(shí),有相應的安全機制來(lái)保護它,當數據被帶出傳統的安全邊界時(shí),需要有同等的安全機制。
當你緩存數據時(shí),應用系統需要的維護工作加大了。在發(fā)布應用程序時(shí),需要配置相應的屬性,比如緩存的大小限制和清除策略。同時(shí)要使用某種機制來(lái)監控緩存的效率(比如事件日志和性能計數器)
第一節內容簡(jiǎn)單介紹了緩存技術(shù)中的概念、緩存數據的原因和方案、優(yōu)勢、實(shí)施緩存方案時(shí)的考慮等基本內容?,F在你對緩存中涉及的內容有了一個(gè)大致了解,下面著(zhù)重介紹可用的緩存技術(shù)。
本節將介紹以下技術(shù):
使用Asp.NET緩存;
使用Remoting Singleton緩存;
使用內存映射文件;
使用SQL Server緩存;
使用靜態(tài)變量緩存;
使用Asp.Net 會(huì )話(huà)狀態(tài)(Session State);
使用Asp.net客戶(hù)端緩存和狀態(tài);
使用Internet Explorer緩存。
將常用的數據保存在內存中對asp的開(kāi)發(fā)人員來(lái)說(shuō)并不陌生,Session對象和Application對象提供鍵值對來(lái)緩存數據,Session對象保存和單個(gè)用戶(hù)有關(guān)的數據,Application對象可保留和應用程序有關(guān)的數據,每個(gè)用戶(hù)都可以訪(fǎng)問(wèn)。
在Asp.net中,提供了專(zhuān)門(mén)用于緩存數據的Cache對象,它的應用范圍是應用程序域。生存期是和應用程序緊密相關(guān)的,每當應用程序啟動(dòng)的時(shí)候就重新創(chuàng )建Cache對象。它域Application對象的主要區別就是提供了專(zhuān)門(mén)用于緩存管理的特性,比如依賴(lài)和過(guò)期策略。
你可以使用Cache對象和它的屬性來(lái)實(shí)現高級的緩存功能,同時(shí)可以利用Asp.net Cache來(lái)對客戶(hù)端輸出的響應內容進(jìn)行緩存。關(guān)于Asp.net中的緩存技術(shù),有以下內容要介紹:
Cache對象定義在System.Web.Caching命名空間,可以使用HttpContext類(lèi)的Cache屬性或Page對象的Cache屬性來(lái)得到Cache的引用,Cache對象除了存儲鍵值對以外,還可以存儲.net框架的對象。下面介紹相應的依賴(lài)和過(guò)期策略。
當向緩存中加數據時(shí),可以指定它的依賴(lài)關(guān)系來(lái)實(shí)現在某些情況下強制移除它??捎玫姆桨赴ㄒ韵聨追N:
·文件依賴(lài)(File Dependency)——當硬盤(pán)上的某個(gè)(某些)文件更改時(shí),強制移除緩存數據;如:
CacheDependency cDependency = new
CacheDependency(Server.MapPath("authors.xml"));
Cache.Insert("CachedItem", item, cDependency);
·鍵值依賴(lài)(Key Dependency)——指定緩存中的某個(gè)數據項更改時(shí)移除。如:
// Create a cache entry.
Cache["key1"] = "Value 1";
// Make key2 dependent on key1.
String[] dependencyKey = new String[1];
dependencyKey[0] = "key1";
CacheDependency dependency = new CacheDependency(null, dependencyKey);
Cache.Insert("key2", "Value 2", dependency);
·基于時(shí)間的過(guò)期策略——按照預先定義的時(shí)間策略來(lái)使數據失效,可以是絕對時(shí)間(如某個(gè)日期的18:00)也可以是相對現在的相對時(shí)間。如:
/// Absolute expiration
Cache.Insert("CachedItem", item, null, DateTime.Now.AddSeconds(5),Cache.NoSlidingExpiration);
/// Sliding expiration
Cache.Insert("CachedItem", item, null, Cache.NoAbsoluteExpiration,
TimeSpan.FromSeconds(5));
使用太短和太長(cháng)的過(guò)期時(shí)間都不行,不是造成用不上的緩存數據,就是緩存了陳舊的數據并加重了緩存負擔,所以可以使用高并發(fā)的測試來(lái)決定過(guò)期時(shí)間的最佳值。
·另外有個(gè)問(wèn)題就是如何實(shí)現對數據庫的依賴(lài),這就要求實(shí)現自己的通知機制,當數據庫數據改變時(shí)能夠通知你的緩存數據改變??蓞⒖?/span>http://www.gotdotnet.com/team/rhoward的示例。
由于數據會(huì )過(guò)期,所以當使用緩存中的數據時(shí),必須檢查數據的有效性。如以下代碼:
string data = (string)Cache["MyItem"];
if (data == null)
{
data = GetData();
Cache.Insert("MyItem", data);
}
DoSomeThingWithData(data);
依賴(lài)和過(guò)期策略指定了緩存中數據的移除方式,有時(shí)候你可能需要在移除發(fā)生時(shí)做一些工作,這能靠寫(xiě)代碼來(lái)實(shí)現這一點(diǎn),這就是我們要講到的。
你可以定義回調,這樣當移除自動(dòng)發(fā)生時(shí), 你可以不移除它或者使用新的數據來(lái)替換它。如:
CacheItemRemovedCallback onRemove = new CacheItemRemovedCallback(this.RemovedCallback);
Cache.Insert("CachedItem",
item,
null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
onRemove);
// Implement the function to handle the expiration of the cache.
public void RemovedCallback(string key, object value, CacheItemRemovedReason r)
{
// Test whether the item is expired, and reinsert it into the cache.
if (r == CacheItemRemovedReason.Expired)
{
// Reinsert it into the cache again.
CacheItemRemovedCallback onRemove = null;
onRemove = new CacheItemRemovedCallback(this.RemovedCallback);
Cache.Insert(key,
value,
null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
onRemove);
}
}
當運行應用程序的服務(wù)器內存不足時(shí),會(huì )自動(dòng)清除緩存中的數據,稱(chēng)為“清除scavenging”。此時(shí),Cache對象根據緩存項的優(yōu)先級來(lái)決定先移除哪些緩存數據,你可以在代碼中指定緩存項的優(yōu)先級。參看MSDN中“CacheItemPriority 枚舉”,如:
Cache.Insert("DSN", connectionString, null, d, t, CacheItemPriority.High, onRemove);
沒(méi)有直接的方法來(lái)刷新Asp.net的輸出緩存,但是有替代方法(設置所有數據失效),比如:
Response.Cache.SetExpires(DateTime.Now)
這可以清除緩存,但頁(yè)面上并不立刻體現出來(lái),直到最初的緩存期結束,比如:
<%@ OutputCache Duration="10" VaryByParam="none" %>指令指定的緩存只會(huì )在10秒后才清除。通常并不需要清除所有緩存項,你只要重新加載數據更新緩存就夠了。
你可以使用兩種方式的輸出緩存來(lái)緩存需要傳輸和顯示到客戶(hù)端瀏覽器上的數據——頁(yè)面輸出緩存(Page Output Cache)和頁(yè)面片斷緩存(Page Fragment Cache)。當整個(gè)頁(yè)面相對變化較少時(shí),可以緩存整個(gè)頁(yè)面;如果只是頁(yè)面的一部分經(jīng)常變化,可以使用片斷緩存。
Page Output Caching將對頁(yè)面請求的響應放入緩存中,后續對此頁(yè)面的請求將直接從緩存中得到信息而不是重建此頁(yè)面??梢酝ㄟ^(guò)添加Page指令(高級別,聲明實(shí)現)來(lái)實(shí)現,也可以使用HTTPCachePolicy類(lèi)來(lái)實(shí)現(低級別,程序實(shí)現)。本指南不打算介紹技術(shù)細節,只給出如何更好使用的指南和最佳實(shí)踐。有四方面的內容:
1、 決定緩存的內容
頁(yè)面輸出緩存可緩存各種信息,緩存這些信息意味著(zhù)你不需要經(jīng)常處理同樣的數據和結果,包括:
·經(jīng)常被請求但不不改變的靜態(tài)頁(yè)面;
·更新頻率和時(shí)間已知的頁(yè)面(如顯示股票價(jià)格的頁(yè)面);
·根據HTTP參數,有幾個(gè)可能輸出的頁(yè)面(如根據城市的代號顯示該城市天氣情況的頁(yè)面);
·從Web Service返回的結果;如:
[WebMethod(CacheDuration=60)]
public string HelloWorld()
{
return "Hello World";
}
2、 緩存動(dòng)態(tài)頁(yè)面
基于輸入參數、語(yǔ)言和瀏覽器類(lèi)型改變的動(dòng)態(tài)網(wǎng)頁(yè)經(jīng)常用到。你可以使用OutputCache的以下屬性來(lái)實(shí)現對動(dòng)態(tài)頁(yè)面的緩存:
VaryByParam——基于輸入參數不同緩存同一頁(yè)面的多個(gè)版本;
VaryByHeader——基于Page Header的內容不同緩存頁(yè)面的多個(gè)版本;
VaryByCustom——通過(guò)聲明屬性和重載GetVaryByCustomString方法來(lái)定制緩存處理頁(yè)面的多個(gè)版本;
VaryByControl——基于控件中asp對象屬性的不同來(lái)緩存控件。
對多個(gè)版本頁(yè)面的緩存會(huì )降低可用內存,所以要仔細衡量緩存策略。s
3、 控制緩存的位置
你可以使用@OutputCache指令的OutputCacheLocation屬性的枚舉值來(lái)指定緩存的位置,如:
<%@ outputcache duration="10" varybyparam="none" Location="Server" %>
4、 配置頁(yè)面輸出緩存
有兩種方式控制,你可以使用Page指令,也可以使用Cache API編程實(shí)現。參考以下兩段代碼:
//代碼1,使用指令
<%@ OutputCache Duration="20" Location="Server" VaryByParam="state" VaryByCustom="minorversion" VaryByHeader="Accept-Language"%>
//代碼2,編程實(shí)現
private void Page_Load(object sender, System.EventArgs e)
{
// Enable page output caching.
Response.Cache.SetCacheability(HttpCacheability.Server);
// Set the Duration parameter to 20 seconds.
Response.Cache.SetExpires(System.DateTime.Now.AddSeconds(20));
// Set the Header parameter.
Response.Cache.VaryByHeaders["Accept-Language"] = true;
// Set the cached parameter to 'state'.
Response.Cache.VaryByParams["state"] = true;
// Set the custom parameter to 'minorversion'.
Response.Cache.SetVaryByCustom("minorversion");
…
}
有時(shí)候緩存整個(gè)頁(yè)面并不靈活,同時(shí)內存的發(fā)但也比較大,這時(shí)候應考慮片斷緩存。頁(yè)面片斷緩存適合以下類(lèi)型的數據:
·創(chuàng )建開(kāi)銷(xiāo)很大的頁(yè)面片斷(控件);
·包含靜態(tài)數據的頁(yè)面片斷;
·可被多個(gè)用戶(hù)使用的頁(yè)面片斷;
·多個(gè)頁(yè)面共享的頁(yè)面片斷(如公用菜單條)
以下是緩存部分頁(yè)面的例子:
// Partial caching for 120 seconds
[System.Web.UI.PartialCaching(120)]
public class WebUserControl : System.Web.UI.UserControl
{
// Your Web control code
}
Asp.net Cache位于System.Web命名空間,但由于它是一個(gè)通用的方案,所以仍然可以在引用此命名空間的任何非Web項目中使用它。
System.Web.Caching.Cache 類(lèi)是對象的緩存,它可以通過(guò)System.Web.HttpRuntime.Cache 的靜態(tài)屬性或System.Web.UI.Page 和System.Web.HttpContext.Cache來(lái)訪(fǎng)問(wèn)。因此在請求上下文之外也可以存在,在每個(gè)應用程序域中只有一個(gè)實(shí)例,所以HttpRuntime.Cache對象可以在Aspnet_wp.exe之外的每個(gè)應用程序域中存在。以下代碼演示了在普通應用里訪(fǎng)問(wèn)Cache對象:
HttpRuntime httpRT = new HttpRuntime();
Cache cache = HttpRuntime.Cache;
.Net Remoting提供了跨應用程序域、跨進(jìn)程、跨計算機的程序運行框架。服務(wù)器激活的對象有兩種激活模式,其中Singleton 類(lèi)型任何時(shí)候都不會(huì )同時(shí)具有多個(gè)實(shí)例。如果存在實(shí)例,所有客戶(hù)端請求都由該實(shí)例提供服務(wù)。如果不存在實(shí)例,服務(wù)器將創(chuàng )建一個(gè)實(shí)例,而所有后繼的客戶(hù)端請求都將由該實(shí)例來(lái)提供服務(wù)。由于 Singleton類(lèi)型具有關(guān)聯(lián)的默認生存期,即使任何時(shí)候都不會(huì )有一個(gè)以上的可用實(shí)例,客戶(hù)端也不會(huì )總接收到對可遠程處理的類(lèi)的同一實(shí)例的引用。所以將數據緩存起來(lái)可以在多個(gè)客戶(hù)端之間共享狀態(tài)信息。
為了使用.Net Remoting實(shí)現緩存方案,要保證遠程對象的租約不過(guò)期,并且遠程對象沒(méi)有被垃圾回收器銷(xiāo)毀(對象租約是指在系統刪除該對象前它在內存中的生存期)。當實(shí)現緩存時(shí),重載MarshalByRefObject的InitializeLifetimeService方法并且返回null,這樣就能保證租約永遠不過(guò)期并且相關(guān)的對象生存期是無(wú)限的。以下代碼是一個(gè)示例:
public class DatasetStore : MarshalByRefObject
{
// A hash table-based data store
private Hashtable htStore = new Hashtable();
//Returns a null lifetime manager so that GC won't collect the object
public override object InitializeLifetimeService() { return null; }
// Your custom cache interface
}
注意:由于這種方案的成本較高、性能上的限制并且可能造成系統不穩定,通常采用基于Sql Server的方案來(lái)替代。
內存映射文件提供獨一無(wú)二的特性,允許應用程序通過(guò)指針來(lái)訪(fǎng)問(wèn)磁盤(pán)上的文件——與訪(fǎng)問(wèn)動(dòng)態(tài)內存趣的方式一樣。所以你可以將應用程序進(jìn)程中的某個(gè)地址段的數據映射到文件中,供多個(gè)跨應用程序域或跨進(jìn)程訪(fǎng)問(wèn)。
在windows中,代碼和數據是以以種方式處理的,表現形式都是內存頁(yè),而在內存頁(yè)背后都是磁盤(pán)上的文件。唯一的不同磁盤(pán)上的文件類(lèi)型不同。代碼后面是可執行的鏡像,而數據后面則是系統的頁(yè)面文件。當多個(gè)應用程序共享內存時(shí),系統的性能會(huì )有明顯提升。
你可以使用內存映射文件的這種特性來(lái)實(shí)現同一臺機器上的跨進(jìn)程和跨應用程序域的緩存解決方案?;趦却嬗成湮募木彺娣桨赴韵陆M件:
·windows NT服務(wù)——啟動(dòng)時(shí)創(chuàng )建內存映射文件,停止時(shí)刪除它。功能是向使用緩存的進(jìn)程提供句柄。當然,也可以使用命名的內存映射文件來(lái)提供操作接口;
·緩存托管組件(Cache Management Dll)——實(shí)現特定的緩存功能,比如:
a. 插入和刪除數據項到緩存中;
b. 使用算法清除緩存,比如最后使用算法(Least Recently Used);
c. 保證數據不被篡改;
基于內存映射文件的緩存方案可以用在應用程序的每個(gè)層中,但由于使用win32 API調用,所以并不容易實(shí)現。.Net 框架不支持內存映射文件,所以只能以非托管代碼的方式運行,當然也不能利用.Net框架的有力特性,比如垃圾回收等。同時(shí)緩存數據項的管理功能需要定制開(kāi)發(fā),還要開(kāi)發(fā)性能計數器來(lái)監控緩存的效果。
如果需要在進(jìn)程回收(重啟)、機器重啟或電源故障的過(guò)程中保持緩存數據的有效,基于內存的方案并不能滿(mǎn)足要求。你可以使用基于永久數據存儲的方案,如SQL server數據庫或NTFS文件系統。
SQL Server在使用sql語(yǔ)句或存儲過(guò)程得到數據時(shí),對varchar和varBinary類(lèi)型的數據有8k的大小限制,你必須使用.Net 框架提供的Ado.Net SQLDataAdapter對象來(lái)訪(fǎng)問(wèn)datatable或dataset。
使用SQL Server緩存數據的優(yōu)點(diǎn):
·易于實(shí)現——使用.Net 框架和Ado.Net訪(fǎng)問(wèn)數據庫相當方便;
·完善的安全模型和很高的健壯性;
·數據非常方便的共享;
·數據的持久保留。
·支持很大的數據量。
·方便的管理工具
當然,也有缺點(diǎn):
·需要安裝SQL Server,對小型應用來(lái)說(shuō)不合適;
·重新構造數據的性能和讀取數據庫的性能比較;
·網(wǎng)絡(luò )負擔。
靜態(tài)變量常用來(lái)記錄類(lèi)的狀態(tài),你可以用它來(lái)創(chuàng )建定制的緩存對象。在定制的緩存類(lèi)中將你的數據存儲器聲明為靜態(tài)變量,并且提供維護接口(插入、刪除和訪(fǎng)問(wèn)等)。如果沒(méi)有特殊的緩存需求(比如依賴(lài)、失效策略等),使用靜態(tài)變量緩存數據是很方便的。由于是在內存中,這種方案可提供對緩存數據的直接、高速的訪(fǎng)問(wèn),當沒(méi)有替代方案解決鍵值對的存儲且對速度要求很高時(shí),可以使用靜態(tài)變量。當然,在asp.net中,應該使用Cache對象。
你可以使用這種方案保存大數據的對象,前提是它不經(jīng)常更改。由于沒(méi)有清除機制,大數據的內存消耗會(huì )影響性能。
你需要保證定制線(xiàn)程安全機制,或者使用.Net框架提供的同步對象,比如Hashtable。以下代碼是使用Hashtable實(shí)現的例子:
static Hashtable mCacheData = new Hashtable();
應用范圍:本方案的應用范圍可以限制到類(lèi)、模塊或整個(gè)項目。如果變量定義為public,整個(gè)項目中的代碼都能訪(fǎng)問(wèn)它,范圍是整個(gè)應用程序域,實(shí)現了高效的共享。而它的生存期是和范圍緊密相關(guān)的。
你可以使用基于HttpSessionState對象的asp.net session state來(lái)緩存單個(gè)用戶(hù)的會(huì )話(huà)狀態(tài)信息。它解決了asp中會(huì )話(huà)狀態(tài)的很多限制,包括:
·asp session要求客戶(hù)端接受cookies,否則就不能使用session;而asp.net可以配置為不使用cookie;
·對web server場(chǎng)的情況,asp的session不能支持;當穩定性和可用性要求很高時(shí),asp.net session state雖然效果不好,但對比較小的單個(gè)值scalar Value(比如登錄信息),還是很有效。
Asp.net session有很大改進(jìn),下面描述使用范圍和使用方式。
Asp.net session state有三種操作模式:
1、 進(jìn)程內模式InProc——Session State信息在asp.net工作進(jìn)程aspnet_wp.exe的進(jìn)程的內存中存儲。這是默認選項,這種情況下,如果進(jìn)程或應用程序域被回收,則Session 狀態(tài)信息也被回收;
2、 進(jìn)程外模式State Server——狀態(tài)信息序列化后保存在獨立的狀態(tài)進(jìn)程中(AspNet_State.exe),所以狀態(tài)信息可以保存在專(zhuān)門(mén)的服務(wù)器上(一個(gè)狀態(tài)服務(wù)器State Server);
3、 Sql server模式——狀態(tài)信息序列化后保存在SQL Server數據庫中。
你可以通過(guò)調整配置文件中<sessionState>標簽的mode屬性來(lái)設置要使用的狀態(tài)模式,比如使用SQL Server模式來(lái)在Web server場(chǎng)中共享狀態(tài)信息。當然,這個(gè)優(yōu)勢也有缺點(diǎn),就是狀態(tài)信息需要序列化和反序列化,同時(shí)多了對數據庫的寫(xiě)入和讀取,所以性能上有開(kāi)銷(xiāo),這是要仔細評估的。
當使用進(jìn)程內模式時(shí),狀態(tài)信息保存在aspnet_wp.exe的進(jìn)程中。由于在web場(chǎng)的情況下aspnet_wp.exe的多個(gè)實(shí)例在同一臺服務(wù)器上運行,所以進(jìn)程內模式不適用與web場(chǎng)的情況。
進(jìn)程內模式是唯一支持Session_End事件的session模式,當用戶(hù)會(huì )話(huà)超時(shí)或中止時(shí),可以運行Session_End中的事件處理代碼來(lái)清除資源。
StateServer模式使用指定的進(jìn)程儲存狀態(tài)信息。因為它也是一種進(jìn)程外模式,所以要保證你存儲的對象是可序列化的,以支持跨進(jìn)程傳輸。
當使用Session對象在web場(chǎng)的情況下使用時(shí),必須保證web.config文件中的<MachineKey>元素在所有服務(wù)器上是唯一的。這樣所有的服務(wù)器使用同樣的加密方式,才能訪(fǎng)問(wèn)緩存中的數據。參考msdn中的“MachineKey元素”。
SQL Server模式下,當你使用信任連接(trusted_connection=true 或 integrated security=sspi)訪(fǎng)問(wèn)Session state信息時(shí),不能在asp.net中使用身份用戶(hù)模擬。
默認情況下,SQL Server將狀態(tài)信息存儲在TempDb數據庫中,它在每次Sql server服務(wù)啟動(dòng)時(shí)會(huì )自動(dòng)重新創(chuàng )建,當然,你可以指定自己的數據庫以便在數據庫重啟的過(guò)程中也能保持數據。
你可以使用Session對象緩存任何類(lèi)型的.net框架數據,但是要了解對某種類(lèi)型來(lái)說(shuō)最好的方式是什么。有以下幾點(diǎn)需要說(shuō)明:
1、 對基本類(lèi)型(比如Int,Byte,String)來(lái)說(shuō),可以使用任何方式。因為在選用進(jìn)程外方式時(shí),asp.net使用一個(gè)優(yōu)化的內部方法來(lái)序列化和反序列化基本類(lèi)型的數據;
2、 對復雜類(lèi)型(如ArrayList)來(lái)說(shuō),只選用進(jìn)程內方式。因為asp.net使用BinaryFormatter來(lái)序列化和反序列化這類(lèi)數據,而這會(huì )影響性能的。當然,只有在State Server和SQL Server的方式下,才會(huì )進(jìn)行序列化操作;
3、 緩存的安全問(wèn)題,當在緩存中存儲敏感數據時(shí),需要考慮安全性,其它頁(yè)面可以訪(fǎng)問(wèn)到緩存中的數據;
4、 避免緩存大數據,那會(huì )降低性能;
5、 這種緩存方式不支持過(guò)期策略、清除和依賴(lài)。
Asp.net提供了簡(jiǎn)單接口來(lái)操作Session State,并可使用Web.Config進(jìn)行簡(jiǎn)單設置,當配置文件中的設置改變時(shí),能夠在頁(yè)面上立刻體現出來(lái),而不需要重新啟動(dòng)asp.net進(jìn)程。
以下代碼演示了使用SQL Server來(lái)實(shí)現Session數據的存儲和使用。
<sessionState
mode="SQLServer"
stateConnectionString="tcpip=127.0.0.1:42424"
sqlConnectionString="data source=127.0.0.1; Integrated Security=SSPI"
cookieless="false"
timeout="20"
/>
private void SaveSession(string CartID)
{
Session["ShoppingCartID"] = CartID;
}
private void CheckOut()
{
string CartID = (string)Session["ShoppingCartID"];
if(CartID != null)
{
// Transfer execution to payment page.
Server.Transfer("Payment.aspx");
}
else
{
// Display error message.
}
}
你還可以使用客戶(hù)端存儲頁(yè)面信息的方式來(lái)降低服務(wù)器的負擔,這種方法提供最低的安全保障,但卻有最快的性能表現。由于需要將數據發(fā)送到客戶(hù)端存儲,所以數據量有限。
實(shí)現客戶(hù)端緩存的機制有以下五種,接下來(lái)將依次介紹:
·隱藏欄位(Hidden Field)
·View State
·隱藏幀(Hidden Frame)
·Cookies
·Query String
這五種方式分別適合于存儲不同類(lèi)型的數據。
你可以將經(jīng)常改變的少量數據保存在HtmlInputHidden中來(lái)維護頁(yè)面的狀態(tài)。當每次頁(yè)面回送的過(guò)程中,這些數據都會(huì )包含在表單中大送到服務(wù)器,所以你要使用HTTP POST方式來(lái)提交頁(yè)面。
使用這種方式的優(yōu)點(diǎn)如下:
缺點(diǎn):
示例:
<input id="HiddenValue" type="hidden" value="Initial Value" runat="server" NAME="HiddenValue">
所有的Web Form頁(yè)面和控件都包含有一個(gè)ViewState屬性,在對同一頁(yè)面多次請求時(shí)可以保持頁(yè)面內的值。它的內部實(shí)現是維護相應的hidden field,只不過(guò)是加密了的,所以比hidden field的安全性要好。
使用View State的性能表現很大程度上依賴(lài)于服務(wù)器控件的類(lèi)型。一般來(lái)說(shuō),Label,TextBox,CheckBox,RadioButton,HyperLink的性能要好一些,而DropdownList,ListBox,DataGrid和DataList就要差很多,因為包含的數據量太大,所以每次頁(yè)面回送都很耗時(shí)間。
有些情況下不推薦使用ViewState,比如:
1、 不需要回送的頁(yè)面避免使用;
2、 避免使用ViewState保存大數據量;
3、 在需要使用會(huì )話(huà)超時(shí)的情況下避免使用它,因為它沒(méi)有超時(shí)操作。
ViewState的性能表現和Hidden Field的是類(lèi)似的,但是具有更高的安全性。
優(yōu)點(diǎn):
缺點(diǎn):
示例代碼如下:
public class ViewStateSample : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack)
{
// Save some data in the ViewState property.
this.ViewState["EnterTime"] = DateTime.Now.ToString();
this.ViewState["UserName"] = "John Smith";
this.ViewState["Country"] = "
}
}
…
private void btnRefresh_Click(object sender, System.EventArgs e)
{
// Get the saved data in the view state and display it.
this.lblTime.Text = this.ViewState["EnterTime"].ToString();
this.lblUserName.Text = this.ViewState["UserName"].ToString();
this.lblCountry.Text = this.ViewState["Country"].ToString();
}
}
你可以使用Hidden Frame在客戶(hù)端緩存數據,這就避免了使用hidden field和使用view state時(shí)每次頁(yè)面回送時(shí)的緩存數據往返。比如你可以秘密的加載多個(gè)頁(yè)面所需要的圖片,這并不會(huì )消耗服務(wù)器資源。
優(yōu)點(diǎn):
a. 可以加載較多數據而不只是單個(gè)欄位的值;
b. 避免了不必要的多次回送中的數據往來(lái);
c. 可以緩存和讀取在不同表單中存儲的數據項(可以同時(shí)緩存多個(gè)頁(yè)面的數據);
d. 可以訪(fǎng)問(wèn)同一站點(diǎn)不同frame中的客戶(hù)端腳本數據。
缺點(diǎn):
a. 有些瀏覽器不支持frame;
b. 源代碼可以在客戶(hù)端看到,有潛在的安全威脅;
c. 隱藏frame的數量沒(méi)有限制,如果框架頁(yè)面包含較多hidden frame的話(huà),在首次加載時(shí)速度會(huì )有限制。
示例代碼如下:
<FRAMESET cols="100%,*">
<FRAMESET rows="100%,*">
<FRAME src="contents_of_frame1.html">
</FRAMESET>
<FRAME src="contents_of_hidden_frame.html">
<FRAME src="contents_of_hidden_frame.html" frameborder="0" noresize scrolling="yes">
<NOFRAMES>
<P>This frameset document contains:
<A href="contents_of_frame1.html" TARGET="_top">Some neat contents</A>
</NOFRAMES>
</FRAMESET>
Cookie是可以在客戶(hù)端存儲數據另一種方案,這里不過(guò)多介紹。
優(yōu)點(diǎn):
缺點(diǎn):
參看示例代碼:
public class CookiesSample : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
if (this.Request.Cookies["preferences1"] == null)
{
HttpCookie cookie = new HttpCookie("preferences1");
cookie.Values.Add("ForeColor","black");
cookie.Values.Add("BackColor","beige");
cookie.Values.Add("FontSize","8pt");
cookie.Values.Add("FontName","Verdana");
this.Response.AppendCookie(cookie);
}
}
private string getStyle(string key)
{
string val = null;
HttpCookie cookie= this.Request.Cookies["preferences1"];
if (cookie != null)
{
val = cookie.Values[key];
}
return val;
}
}
Query String是在用戶(hù)請求的URL后加上相應的參數來(lái)使用的,只能在使用HTTP GET方式調用URL時(shí)可用。
優(yōu)點(diǎn):
d. 不需要服務(wù)器資源,參數附在URL里面;
e. 應用面廣,幾乎所有瀏覽器都支持;
f. 實(shí)現簡(jiǎn)單,服務(wù)端使用Request對象可直接讀取。
缺點(diǎn):
a. 參數直接對用戶(hù)可見(jiàn),不安全;
b. URL長(cháng)度的限制,多數瀏覽器不支持超過(guò)255字符的URL。
示例代碼:
http://www.cache.com/login.asp?user=ronen
string user = Request.QueryString["User"];
下表是使用客戶(hù)端緩存的建議:
緩存機制 | 適用情況 |
Hidden Field | 當安全性要求不高時(shí),在頁(yè)面中存儲少量數據以提交到服務(wù)器上的本頁(yè)面或其它頁(yè)面。 |
ViewState | 在單個(gè)頁(yè)面中存儲少量信息滿(mǎn)足頁(yè)面多次回傳的要求。提供基本的安全機制。 |
Hidden Frame | 在客戶(hù)端存儲數據,避免了數據到服務(wù)器的回傳。 |
Cookie | 當安全性要求不高時(shí),存儲少量數據在客戶(hù)端。 |
Query String | 當使用頁(yè)面地址連接頁(yè)面時(shí)傳輸少量參數。 |
IE提供了緩存機制,可以實(shí)現對頁(yè)面的數據進(jìn)行緩存,同時(shí)可以指定過(guò)期時(shí)間。用戶(hù)在IE中請求此頁(yè)面,如果當過(guò)期時(shí)間沒(méi)有到,則自動(dòng)從緩存中提取并呈現;否則,就到服務(wù)器上獲取新版本。IE對頁(yè)面的緩存可以在IIS中設置。
適合在Internet Explorer中緩存的內容
優(yōu)點(diǎn):
缺點(diǎn):
示例代碼:
<META HTTP-EQUIV="expires" CONTENT="
本文檔介紹了緩存和狀態(tài)數據存儲的相關(guān)概念,以及可供使用的緩存技術(shù),介紹了各種技術(shù)的適用范圍,并對其優(yōu)缺點(diǎn)進(jìn)行了說(shuō)明,另外有簡(jiǎn)單的性能比較和簡(jiǎn)單的示例代碼。更多內容請參看相應的參考資料。
聯(lián)系客服