一、 概念
1.1 緩存能解決的問題
· 性能——將相應數(shù)據(jù)存儲起來以避免數(shù)據(jù)的重復創(chuàng)建、處理和傳輸,可有效提高性能。比如將不改變的數(shù)據(jù)緩存起來,例如國家列表等,這樣能明顯提高web程序的反應速度;
· 穩(wěn)定性——同一個應用中,對同一數(shù)據(jù)、邏輯功能和用戶界面的多次請求時經(jīng)常發(fā)生的。當用戶基數(shù)很大時,如果每次請求都進行處理,消耗的資源是很大的浪費,也同時造成系統(tǒng)的不穩(wěn)定。例如,web應用中,對一些靜態(tài)頁面的呈現(xiàn)內(nèi)容進行緩存能有效的節(jié)省資源,提高穩(wěn)定性。而緩存數(shù)據(jù)也能降低對數(shù)據(jù)庫的訪問次數(shù),降低數(shù)據(jù)庫的負擔和提高數(shù)據(jù)庫的服務能力;
· 可用性——有時,提供數(shù)據(jù)信息的服務可能會意外停止,如果使用了緩存技術,可以在一定時間內(nèi)仍正常提供對最終用戶的支持,提高了系統(tǒng)的可用性。
1.2 理解狀態(tài)
在深入介紹緩存技術之前,需要對狀態(tài)有一個認識,因為緩存可以說是狀態(tài)管理的框架。理解狀態(tài)的含義和它的一些特性——比如生存期和生存范圍——對決定是否緩存和選擇合適的緩存技術有很大幫助。狀態(tài)是指一些數(shù)據(jù),在應用系統(tǒng)某個時間點上,數(shù)據(jù)的狀態(tài)和條件。這些數(shù)據(jù)可能是永久性的存儲在數(shù)據(jù)庫中,可能是只在內(nèi)存里停留一會,也可能是按照某個邏輯存活(比如多長時間后釋放),它的應用范圍可能是所有用戶可訪問,可能是單個用戶有權限;
1.2.1 狀態(tài)的生存期
生存期是指數(shù)據(jù)保持有效性的時間區(qū)間,也就是從創(chuàng)建到移除的時間間隔。通常的生存期有以下幾種:
·永久狀態(tài)Permanent State——應用程序使用的永久數(shù)據(jù);
·進程狀態(tài)Process State——只在進程周期內(nèi)有效;
·會話狀態(tài)Session State——和特定的用戶會話有關;
·消息狀態(tài)Message State——處理某個消息的時間內(nèi)有效;
1.2.2 狀態(tài)的范圍
狀態(tài)的范圍指對該狀態(tài)有訪問權限的物理或邏輯范圍。
·物理范圍指可以被訪問到的狀態(tài)數(shù)據(jù)存放的物理位置,通常包括:
1、 組織Organization——在一個組織內(nèi)的所有應用程序可以訪問狀態(tài)數(shù)據(jù);
2、 場Farm——在應用場范圍內(nèi)的任何機器上都可以訪問;
3、 機器Machine——單個機器范圍內(nèi)可以訪問;
4、 進程Process——進程內(nèi)的訪問許可;
5、 應用域AppDomain——應用程序域內(nèi)的訪問許可。
·邏輯范圍指可訪問狀態(tài)數(shù)據(jù)的邏輯范圍,常見的有:
1、 應用程序Application;
2、 業(yè)務流程Business Process;
3、 角色Role;
4、 用戶User;
1.2.3 狀態(tài)數(shù)據(jù)的陳舊
緩存的狀態(tài)數(shù)據(jù)只是主數(shù)據(jù)(Master State Data)的快照,由于數(shù)據(jù)源可能被修改,所以狀態(tài)數(shù)據(jù)就有會陳舊的特性。合理利用此特性和將數(shù)據(jù)陳舊的負面影響最小化是緩存狀態(tài)數(shù)據(jù)的一個重要任務。你可以以一下方式定義數(shù)據(jù)的陳舊依據(jù):
·主數(shù)據(jù)更改的可能性——隨著時間的推進,主數(shù)據(jù)更改的可能是否大大增加?安照這一點來決定緩存狀態(tài)數(shù)據(jù)的陳舊;
·更改的相關性——主數(shù)據(jù)更新時,緩存的狀態(tài)數(shù)據(jù)不相應更新是不是造成影響系統(tǒng)的使用?比如,更改系統(tǒng)的外觀風格并不會對業(yè)務造成很大影響。
1.2.4 狀態(tài)數(shù)據(jù)陳舊的容忍度
緩存狀態(tài)數(shù)據(jù)的陳舊對業(yè)務流程的影響稱為容忍度,應用系統(tǒng)的可以為不能容忍(No Tolerance)和一定程度的容忍(some Tolerance),前者必須和主數(shù)據(jù)同步更新,后者允許一定時間或一定范圍的陳舊,判斷標準就是對業(yè)務流程的影響度。
1.2.5 理解狀態(tài)數(shù)據(jù)的轉換過程
狀態(tài)的另一個屬性是在不同階段的表現(xiàn)形式。在數(shù)據(jù)庫中存儲的是原始格式的數(shù)據(jù),業(yè)務流程中的是處理過的數(shù)據(jù),給最終用戶呈現(xiàn)的則是另外的形式。如下表所示:
當決定緩存數(shù)據(jù)時,應該考慮緩存哪個階段(哪種形式)的狀態(tài)數(shù)據(jù)。以下方針有助于你做決定:
· 當業(yè)務邏輯可以容忍緩存數(shù)據(jù)的陳舊時就緩存原始數(shù)據(jù);原始數(shù)據(jù)可以緩存在數(shù)據(jù)庫訪問組件和服務代理中;
·緩存處理過的數(shù)據(jù)以減少處理時間和資源;處理過的數(shù)據(jù)可以緩存在業(yè)務邏輯組件和服務接口中。
·當需要呈現(xiàn)的數(shù)據(jù)量很大并且控件的呈現(xiàn)時間很長時,緩存呈現(xiàn)數(shù)據(jù)(比如包含大數(shù)據(jù)量的Treeview控件)。這種數(shù)據(jù)應該被緩存在UI控件中。
1.3 為什么要緩存數(shù)據(jù)
在應用程序中緩存數(shù)據(jù)有以下好處:
·減少交互的通訊量——緩存數(shù)據(jù)能有效減少在進程和機器間的傳輸量;
·降低系統(tǒng)中的處理量——減少處理次數(shù);
·降低需要做的磁盤訪問次數(shù)——比如緩存在內(nèi)存中的數(shù)據(jù)。
1.4 數(shù)據(jù)應該被緩存在哪里
緩存數(shù)據(jù)只是一份主數(shù)據(jù)的拷貝,它可能在內(nèi)存中或以不同的表現(xiàn)形式保存在硬盤上,也就是說,離說句的使用者越近越好。所以,除了考慮要緩存哪些數(shù)據(jù)以外,數(shù)據(jù)緩存在哪里也是一個主要的考量點。這個問題分為以下兩個范圍:
1、 存儲類型Storage Type——數(shù)據(jù)可用的物理存儲位置;
2、 層間的架構元素(Layered architecture elements)——數(shù)據(jù)可用的邏輯存儲位置。
1.4.1 存儲類型
緩存有很多實現(xiàn)方法,所有這些可以被分為兩類,基于內(nèi)存的緩存和基于磁盤的緩存:
1、 內(nèi)存駐留緩存——包含在內(nèi)存中臨時存儲數(shù)據(jù)的所有實現(xiàn)方法,通常在以下情況下使用:
a) 應用程序頻繁使用同樣的數(shù)據(jù);
b) 應用程序需要經(jīng)常獲取數(shù)據(jù);
通過將數(shù)據(jù)保留在內(nèi)存中,你可以有效降低昂貴的磁盤訪問操作,也可以通過將數(shù)據(jù)保留在使用者進程中來最大程度的減少跨進程的數(shù)據(jù)傳輸。
2、 磁盤駐留緩存——這種技術包含所有使用磁盤作為存儲介質(zhì)的緩存技術,如文件和數(shù)據(jù)庫。在以下情況下基于磁盤的緩存是很有效的:
a) 處理大數(shù)據(jù)量時;
b) 應用服務提供的數(shù)據(jù)可能并不是總能使用(比如離線的情況);
c) 緩存的數(shù)據(jù)必須能在進程回收和機器重啟的情況下保持有效;
通過緩存處理過的數(shù)據(jù),你可以有效降低數(shù)據(jù)處理的負擔,同時可減少數(shù)據(jù)交互的代價。
1.4.2 架構間元素
應用程序中的每個邏輯層的組件都會處理數(shù)據(jù),當使用這些組件進行工作時,你需要考慮哪些數(shù)據(jù)可以被緩存起來,還有以哪種方式進行緩存會對程序的整體性能和可用性有幫助,以上的這些元素都可以緩存相應的數(shù)據(jù)。當然,要考慮的遠不止這些。
1.5 實施緩存時的考慮
當設計一個緩存方案時,不但要考慮緩存哪些數(shù)據(jù)、數(shù)據(jù)緩存到哪里,還有其它的因素需要考慮。
1.5.1 格式和訪問模式
當決定是否緩存一個對象時,關于數(shù)據(jù)的格式和訪問機制,你需要考慮三個主要問題:
1、 線程安全——當緩存的內(nèi)容可以被多個線程訪問時,使用某種鎖定機制來保證數(shù)據(jù)不會被兩個線程同時操作;
2、 序列化——將一個對象緩存時,需要將它序列化以便保存,所以包緩存的對象必須支持序列化;
3、 規(guī)格化緩存數(shù)據(jù)——緩存數(shù)據(jù)時,相對于要使用的數(shù)據(jù)格式而言,要保證數(shù)據(jù)的格式是優(yōu)化過的。
1.5.2 內(nèi)容加載
在使用緩存數(shù)據(jù)前,必須將數(shù)據(jù)加載到緩存中,有兩種機制來加載數(shù)據(jù):
·提前加載Proactive Load——使用這種方式時,你提前將所有的狀態(tài)數(shù)據(jù)加載到緩存中,可能在應用程序或線程啟動時進行,然后在應用程序或線程的生存期內(nèi)一直緩存;
·動態(tài)加載Reactive Load——或稱反應式加載,當使用這種方法時,在應用程序請求數(shù)據(jù)時取到數(shù)據(jù),并且將它緩存起來以備后續(xù)使用。
1.5.3 過期策略
另外一個關鍵因素是如何保持緩存數(shù)據(jù)和主數(shù)據(jù)(文件或數(shù)據(jù)庫或其他的應用程序資源)的一致性,你可以定義過期策略來決定緩存中的內(nèi)容,如已經(jīng)緩存的時間或者收到其他資源的通知。
1.5.4 安全性
當緩存數(shù)據(jù)時,需要非常清楚緩存中數(shù)據(jù)的潛在安全威脅。緩存中的數(shù)據(jù)可能會被別的進程訪問或修改,而此進程對主數(shù)據(jù)是沒有權限的。原因是當數(shù)據(jù)存儲在原始位置時,有相應的安全機制來保護它,當數(shù)據(jù)被帶出傳統(tǒng)的安全邊界時,需要有同等的安全機制。
1.5.5 管理
當你緩存數(shù)據(jù)時,應用系統(tǒng)需要的維護工作加大了。在發(fā)布應用程序時,需要配置相應的屬性,比如緩存的大小限制和清除策略。同時要使用某種機制來監(jiān)控緩存的效率(比如事件日志和性能計數(shù)器)
1.6 小結
第一節(jié)內(nèi)容簡單介紹了緩存技術中的概念、緩存數(shù)據(jù)的原因和方案、優(yōu)勢、實施緩存方案時的考慮等基本內(nèi)容?,F(xiàn)在你對緩存中涉及的內(nèi)容有了一個大致了解,下面著重介紹可用的緩存技術。
二、緩存技術
本節(jié)將介紹以下技術:
使用Asp.Net緩存;
使用Remoting Singleton緩存;
使用內(nèi)存映射文件;
使用SQL Server緩存;
使用靜態(tài)變量緩存;
使用Asp.net 會話狀態(tài)(Session State);
使用Asp.net客戶端緩存和狀態(tài);
使用Internet Explorer緩存。
2.1 Asp.net緩存
將常用的數(shù)據(jù)保存在內(nèi)存中對asp的開發(fā)人員來說并不陌生,Session對象和Application對象提供鍵值對來緩存數(shù)據(jù),Session對象保存和單個用戶有關的數(shù)據(jù),Application對象可保留和應用程序有關的數(shù)據(jù),每個用戶都可以訪問。
在Asp.net中,提供了專門用于緩存數(shù)據(jù)的Cache對象,它的應用范圍是應用程序域。生存期是和應用程序緊密相關的,每當應用程序啟動的時候就重新創(chuàng)建Cache對象。它域Application對象的主要區(qū)別就是提供了專門用于緩存管理的特性,比如依賴和過期策略。
你可以使用Cache對象和它的屬性來實現(xiàn)高級的緩存功能,同時可以利用Asp.net Cache來對客戶端輸出的響應內(nèi)容進行緩存。關于Asp.net中的緩存技術,有以下內(nèi)容要介紹:
2.1.1 編程緩存Programmatic Caching
Cache對象定義在System.Web.Caching命名空間,可以使用HttpContext類的Cache屬性或Page對象的Cache屬性來得到Cache的引用,Cache對象除了存儲鍵值對以外,還可以存儲.net框架的對象。下面介紹相應的依賴和過期策略。
2.1.1.1 依賴和過期策略
當向緩存中加數(shù)據(jù)時,可以指定它的依賴關系來實現(xiàn)在某些情況下強制移除它??捎玫姆桨赴ㄒ韵聨追N:
·文件依賴(File Dependency)——當硬盤上的某個(某些)文件更改時,強制移除緩存數(shù)據(jù);如:
CacheDependency cDependency = new
CacheDependency(Server.MapPath("authors.xml"));
Cache.Insert("CachedItem", item, cDependency);
·鍵值依賴(Key Dependency)——指定緩存中的某個數(shù)據(jù)項更改時移除。如:
// 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ù)據(jù)失效,可以是絕對時間(如某個日期的18:00)也可以是相對現(xiàn)在的相對時間。如:
/// 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));
使用太短和太長的過期時間都不行,不是造成用不上的緩存數(shù)據(jù),就是緩存了陳舊的數(shù)據(jù)并加重了緩存負擔,所以可以使用高并發(fā)的測試來決定過期時間的最佳值。
·另外有個問題就是如何實現(xiàn)對數(shù)據(jù)庫的依賴,這就要求實現(xiàn)自己的通知機制,當數(shù)據(jù)庫數(shù)據(jù)改變時能夠通知你的緩存數(shù)據(jù)改變。
由于數(shù)據(jù)會過期,所以當使用緩存中的數(shù)據(jù)時,必須檢查數(shù)據(jù)的有效性。如以下代碼:
string data = (string)Cache["MyItem"];
if (data == null)
{
data = GetData();
Cache.Insert("MyItem", data);
}
DoSomeThingWithData(data);
依賴和過期策略指定了緩存中數(shù)據(jù)的移除方式,有時候你可能需要在移除發(fā)生時做一些工作,這能靠寫代碼來實現(xiàn)這一點,這就是我們要講到的。
2.1.1.2 使用緩存回調(diào)(Cache Callback)
你可以定義回調(diào),這樣當移除自動發(fā)生時, 你可以不移除它或者使用新的數(shù)據(jù)來替換它。如:
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);
}
}
2.1.1.3 對緩存項使用優(yōu)先級
當運行應用程序的服務器內(nèi)存不足時,會自動清除緩存中的數(shù)據(jù),稱為“清除scavenging”。此時,Cache對象根據(jù)緩存項的優(yōu)先級來決定先移除哪些緩存數(shù)據(jù),你可以在代碼中指定緩存項的優(yōu)先級。參看MSDN中“CacheItemPriority 枚舉”,如:
Cache.Insert("DSN", connectionString, null, d, t, CacheItemPriority.High, onRemove);
2.1.1.4 刷新數(shù)據(jù)(清除緩存)
沒有直接的方法來刷新Asp.net的輸出緩存,但是有替代方法(設置所有數(shù)據(jù)失效),比如:
Response.Cache.SetExpires(DateTime.Now)
這可以清除緩存,但頁面上并不立刻體現(xiàn)出來,直到最初的緩存期結束,比如:
%@ OutputCache Duration="10" VaryByParam="none" %>指令指定的緩存只會在10秒后才清除。通常并不需要清除所有緩存項,你只要重新加載數(shù)據(jù)更新緩存就夠了。
2.1.2 輸出緩存(Output Cache)
你可以使用兩種方式的輸出緩存來緩存需要傳輸和顯示到客戶端瀏覽器上的數(shù)據(jù)——頁面輸出緩存(Page Output Cache)和頁面片斷緩存(Page Fragment Cache)。當整個頁面相對變化較少時,可以緩存整個頁面;如果只是頁面的一部分經(jīng)常變化,可以使用片斷緩存。
2.1.2.1 頁面輸出緩存
Page Output Caching將對頁面請求的響應放入緩存中,后續(xù)對此頁面的請求將直接從緩存中得到信息而不是重建此頁面??梢酝ㄟ^添加Page指令(高級別,聲明實現(xiàn))來實現(xiàn),也可以使用HTTPCachePolicy類來實現(xiàn)(低級別,程序實現(xiàn))。本指南不打算介紹技術細節(jié),只給出如何更好使用的指南和最佳實踐。有四方面的內(nèi)容:
1、決定緩存的內(nèi)容
頁面輸出緩存可緩存各種信息,緩存這些信息意味著你不需要經(jīng)常處理同樣的數(shù)據(jù)和結果,包括:
·經(jīng)常被請求但不不改變的靜態(tài)頁面;
·更新頻率和時間已知的頁面(如顯示股票價格的頁面);
·根據(jù)HTTP參數(shù),有幾個可能輸出的頁面(如根據(jù)城市的代號顯示該城市天氣情況的頁面);
·從Web Service返回的結果;如:
[WebMethod(CacheDuration=60)]
public string HelloWorld()
{
return "Hello World";
}
2、緩存動態(tài)頁面
基于輸入?yún)?shù)、語言和瀏覽器類型改變的動態(tài)網(wǎng)頁經(jīng)常用到。你可以使用OutputCache的以下屬性來實現(xiàn)對動態(tài)頁面的緩存:
VaryByParam——基于輸入?yún)?shù)不同緩存同一頁面的多個版本;
VaryByHeader——基于Page Header的內(nèi)容不同緩存頁面的多個版本;
VaryByCustom——通過聲明屬性和重載GetVaryByCustomString方法來定制緩存處理頁面的多個版本;
VaryByControl——基于控件中asp對象屬性的不同來緩存控件。
對多個版本頁面的緩存會降低可用內(nèi)存,所以要仔細衡量緩存策略。s
3、控制緩存的位置
你可以使用@OutputCache指令的OutputCacheLocation屬性的枚舉值來指定緩存的位置,如:
%@ outputcache duration="10" varybyparam="none" Location="Server" %>
4、 配置頁面輸出緩存
有兩種方式控制,你可以使用Page指令,也可以使用Cache API編程實現(xiàn)。參考以下兩段代碼:
//代碼1,使用指令
%@ OutputCache Duration="20" Location="Server" VaryByParam="state" VaryByCustom="minorversion" VaryByHeader="Accept-Language"%>
//代碼2,編程實現(xiàn)
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");
…
}
2.1.2.2 頁面片斷緩存
有時候緩存整個頁面并不靈活,同時內(nèi)存的發(fā)但也比較大,這時候應考慮片斷緩存。頁面片斷緩存適合以下類型的數(shù)據(jù):
·創(chuàng)建開銷很大的頁面片斷(控件);
·包含靜態(tài)數(shù)據(jù)的頁面片斷;
·可被多個用戶使用的頁面片斷;
·多個頁面共享的頁面片斷(如公用菜單條)
以下是緩存部分頁面的例子:
// Partial caching for 120 seconds
[System.Web.UI.PartialCaching(120)]
public class WebUserControl : System.Web.UI.UserControl
{
// Your Web control code
}
2.1.3 在非Web項目中使用Asp.net緩存
Asp.net Cache位于System.Web命名空間,但由于它是一個通用的方案,所以仍然可以在引用此命名空間的任何非Web項目中使用它。
System.Web.Caching.Cache 類是對象的緩存,它可以通過System.Web.HttpRuntime.Cache 的靜態(tài)屬性或System.Web.UI.Page 和System.Web.HttpContext.Cache來訪問。因此在請求上下文之外也可以存在,在每個應用程序域中只有一個實例,所以HttpRuntime.Cache對象可以在Aspnet_wp.exe之外的每個應用程序域中存在。以下代碼演示了在普通應用里訪問Cache對象:
HttpRuntime httpRT = new HttpRuntime();
Cache cache = HttpRuntime.Cache;
2.2 使用Remoting Singleton緩存
.Net Remoting提供了跨應用程序域、跨進程、跨計算機的程序運行框架。服務器激活的對象有兩種激活模式,其中Singleton 類型任何時候都不會同時具有多個實例。如果存在實例,所有客戶端請求都由該實例提供服務。如果不存在實例,服務器將創(chuàng)建一個實例,而所有后繼的客戶端請求都將由該實例來提供服務。由于 Singleton 類型具有關聯(lián)的默認生存期,即使任何時候都不會有一個以上的可用實例,客戶端也不會總接收到對可遠程處理的類的同一實例的引用。所以將數(shù)據(jù)緩存起來可以在多個客戶端之間共享狀態(tài)信息。
為了使用.Net Remoting實現(xiàn)緩存方案,要保證遠程對象的租約不過期,并且遠程對象沒有被垃圾回收器銷毀(對象租約是指在系統(tǒng)刪除該對象前它在內(nèi)存中的生存期)。當實現(xiàn)緩存時,重載MarshalByRefObject的InitializeLifetimeService方法并且返回null,這樣就能保證租約永遠不過期并且相關的對象生存期是無限的。以下代碼是一個示例:
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
}
注意:由于這種方案的成本較高、性能上的限制并且可能造成系統(tǒng)不穩(wěn)定,通常采用基于Sql Server的方案來替代。
2.3 使用內(nèi)存映射文件(Memory-Mapped File)
內(nèi)存映射文件提供獨一無二的特性,允許應用程序通過指針來訪問磁盤上的文件——與訪問動態(tài)內(nèi)存趣的方式一樣。所以你可以將應用程序進程中的某個地址段的數(shù)據(jù)映射到文件中,供多個跨應用程序域或跨進程訪問。
在windows中,代碼和數(shù)據(jù)是以以種方式處理的,表現(xiàn)形式都是內(nèi)存頁,而在內(nèi)存頁背后都是磁盤上的文件。唯一的不同磁盤上的文件類型不同。代碼后面是可執(zhí)行的鏡像,而數(shù)據(jù)后面則是系統(tǒng)的頁面文件。當多個應用程序共享內(nèi)存時,系統(tǒng)的性能會有明顯提升。
你可以使用內(nèi)存映射文件的這種特性來實現(xiàn)同一臺機器上的跨進程和跨應用程序域的緩存解決方案?;趦?nèi)存映射文件的緩存方案包含以下組件:
·windows NT服務——啟動時創(chuàng)建內(nèi)存映射文件,停止時刪除它。功能是向使用緩存的進程提供句柄。當然,也可以使用命名的內(nèi)存映射文件來提供操作接口;
·緩存托管組件(Cache Management Dll)——實現(xiàn)特定的緩存功能,比如:
a. 插入和刪除數(shù)據(jù)項到緩存中;
b. 使用算法清除緩存,比如最后使用算法(Least Recently Used);
c. 保證數(shù)據(jù)不被篡改;
基于內(nèi)存映射文件的緩存方案可以用在應用程序的每個層中,但由于使用win32 API調(diào)用,所以并不容易實現(xiàn)。.Net 框架不支持內(nèi)存映射文件,所以只能以非托管代碼的方式運行,當然也不能利用.Net框架的有力特性,比如垃圾回收等。同時緩存數(shù)據(jù)項的管理功能需要定制開發(fā),還要開發(fā)性能計數(shù)器來監(jiān)控緩存的效果。
2.4使用SQL Server緩存
如果需要在進程回收(重啟)、機器重啟或電源故障的過程中保持緩存數(shù)據(jù)的有效,基于內(nèi)存的方案并不能滿足要求。你可以使用基于永久數(shù)據(jù)存儲的方案,如SQL server數(shù)據(jù)庫或NTFS文件系統(tǒng)。
SQL Server在使用sql語句或存儲過程得到數(shù)據(jù)時,對varchar和varBinary類型的數(shù)據(jù)有8k的大小限制,你必須使用.Net 框架提供的Ado.Net SQLDataAdapter對象來訪問datatable或dataset。
使用SQL Server緩存數(shù)據(jù)的優(yōu)點:
·易于實現(xiàn)——使用.Net 框架和Ado.Net訪問數(shù)據(jù)庫相當方便;
·完善的安全模型和很高的健壯性;
·數(shù)據(jù)非常方便的共享;
·數(shù)據(jù)的持久保留。
·支持很大的數(shù)據(jù)量。
·方便的管理工具
當然,也有缺點:
·需要安裝SQL Server,對小型應用來說不合適;
·重新構造數(shù)據(jù)的性能和讀取數(shù)據(jù)庫的性能比較;
·網(wǎng)絡負擔。
2.5 使用靜態(tài)變量緩存
靜態(tài)變量常用來記錄類的狀態(tài),你可以用它來創(chuàng)建定制的緩存對象。在定制的緩存類中將你的數(shù)據(jù)存儲器聲明為靜態(tài)變量,并且提供維護接口(插入、刪除和訪問等)。如果沒有特殊的緩存需求(比如依賴、失效策略等),使用靜態(tài)變量緩存數(shù)據(jù)是很方便的。由于是在內(nèi)存中,這種方案可提供對緩存數(shù)據(jù)的直接、高速的訪問,當沒有替代方案解決鍵值對的存儲且對速度要求很高時,可以使用靜態(tài)變量。當然,在asp.net中,應該使用Cache對象。
你可以使用這種方案保存大數(shù)據(jù)的對象,前提是它不經(jīng)常更改。由于沒有清除機制,大數(shù)據(jù)的內(nèi)存消耗會影響性能。
你需要保證定制線程安全機制,或者使用.Net框架提供的同步對象,比如Hashtable。以下代碼是使用Hashtable實現(xiàn)的例子:
static Hashtable mCacheData = new Hashtable();
應用范圍:本方案的應用范圍可以限制到類、模塊或整個項目。如果變量定義為public,整個項目中的代碼都能訪問它,范圍是整個應用程序域,實現(xiàn)了高效的共享。而它的生存期是和范圍緊密相關的。
2.6 使用asp.net session state
你可以使用基于HttpSessionState對象的asp.net session state來緩存單個用戶的會話狀態(tài)信息。它解決了asp中會話狀態(tài)的很多限制,包括:
·asp session要求客戶端接受cookies,否則就不能使用session;而asp.net可以配置為不使用cookie;
·對web server場的情況,asp的session不能支持;當穩(wěn)定性和可用性要求很高時,asp.net session state雖然效果不好,但對比較小的單個值scalar Value(比如登錄信息),還是很有效。
Asp.net session有很大改進,下面描述使用范圍和使用方式。
Asp.net session state有三種操作模式:
1、進程內(nèi)模式InProc——Session State信息在asp.net工作進程aspnet_wp.exe的進程的內(nèi)存中存儲。這是默認選項,這種情況下,如果進程或應用程序域被回收,則Session 狀態(tài)信息也被回收;
2、進程外模式State Server——狀態(tài)信息序列化后保存在獨立的狀態(tài)進程中(AspNet_State.exe),所以狀態(tài)信息可以保存在專門的服務器上(一個狀態(tài)服務器State Server);
3、 Sql server模式——狀態(tài)信息序列化后保存在SQL Server數(shù)據(jù)庫中。
你可以通過調(diào)整配置文件中sessionState>標簽的mode屬性來設置要使用的狀態(tài)模式,比如使用SQL Server模式來在Web server場中共享狀態(tài)信息。當然,這個優(yōu)勢也有缺點,就是狀態(tài)信息需要序列化和反序列化,同時多了對數(shù)據(jù)庫的寫入和讀取,所以性能上有開銷,這是要仔細評估的。
2.6.1 選擇使用模式
2.6.1.1 使用InProc模式
當使用進程內(nèi)模式時,狀態(tài)信息保存在aspnet_wp.exe的進程中。由于在web場的情況下aspnet_wp.exe的多個實例在同一臺服務器上運行,所以進程內(nèi)模式不適用與web場的情況。
進程內(nèi)模式是唯一支持Session_End事件的session模式,當用戶會話超時或中止時,可以運行Session_End中的事件處理代碼來清除資源。
2.6.1.2 使用StateServer模式
StateServer模式使用指定的進程儲存狀態(tài)信息。因為它也是一種進程外模式,所以要保證你存儲的對象是可序列化的,以支持跨進程傳輸。
當使用Session對象在web場的情況下使用時,必須保證web.config文件中的MachineKey>元素在所有服務器上是唯一的。這樣所有的服務器使用同樣的加密方式,才能訪問緩存中的數(shù)據(jù)。參考msdn中的“MachineKey元素”。
2.6.1.3 使用SQL Server模式
SQL Server模式下,當你使用信任連接(trusted_connection=true 或 integrated security=sspi)訪問Session state信息時,不能在asp.net中使用身份用戶模擬。
默認情況下,SQL Server將狀態(tài)信息存儲在TempDb數(shù)據(jù)庫中,它在每次Sql server服務啟動時會自動重新創(chuàng)建,當然,你可以指定自己的數(shù)據(jù)庫以便在數(shù)據(jù)庫重啟的過程中也能保持數(shù)據(jù)。
2.6.2 決定使用Session對象要存儲的內(nèi)容
你可以使用Session對象緩存任何類型的.net框架數(shù)據(jù),但是要了解對某種類型來說最好的方式是什么。有以下幾點需要說明:
1、 對基本類型(比如Int,Byte,String)來說,可以使用任何方式。因為在選用進程外方式時,asp.net使用一個優(yōu)化的內(nèi)部方法來序列化和反序列化基本類型的數(shù)據(jù);
2、 對復雜類型(如ArrayList)來說,只選用進程內(nèi)方式。因為asp.net使用BinaryFormatter來序列化和反序列化這類數(shù)據(jù),而這會影響性能的。當然,只有在State Server和SQL Server的方式下,才會進行序列化操作;
3、 緩存的安全問題,當在緩存中存儲敏感數(shù)據(jù)時,需要考慮安全性,其它頁面可以訪問到緩存中的數(shù)據(jù);
4、 避免緩存大數(shù)據(jù),那會降低性能;
5、 這種緩存方式不支持過期策略、清除和依賴。
2.6.3 實現(xiàn)Session State
Asp.net提供了簡單接口來操作Session State,并可使用Web.Config進行簡單設置,當配置文件中的設置改變時,能夠在頁面上立刻體現(xiàn)出來,而不需要重新啟動asp.net進程。
以下代碼演示了使用SQL Server來實現(xiàn)Session數(shù)據(jù)的存儲和使用。
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.
}
}
2.7使用Asp.net客戶端緩存和狀態(tài)
你還可以使用客戶端存儲頁面信息的方式來降低服務器的負擔,這種方法提供最低的安全保障,但卻有最快的性能表現(xiàn)。由于需要將數(shù)據(jù)發(fā)送到客戶端存儲,所以數(shù)據(jù)量有限。
實現(xiàn)客戶端緩存的機制有以下五種,接下來將依次介紹:
·隱藏欄位(Hidden Field)
·View State
·隱藏幀(Hidden Frame)
·Cookies
·Query String
這五種方式分別適合于存儲不同類型的數(shù)據(jù)。
2.7.1 使用Hidden Field
你可以將經(jīng)常改變的少量數(shù)據(jù)保存在HtmlInputHidden中來維護頁面的狀態(tài)。當每次頁面回送的過程中,這些數(shù)據(jù)都會包含在表單中大送到服務器,所以你要使用HTTP POST方式來提交頁面。
使用這種方式的優(yōu)點如下:
不需要服務器資源,直接從頁面中讀??;
幾乎所有的瀏覽器都支持;
實現(xiàn)簡單;
由于數(shù)據(jù)在頁面中,所以在web Farm的情況下也可使用。
缺點:
由于可以通過查看源碼看到,所以可能會被篡改;
不支持復雜格式的數(shù)據(jù),復雜數(shù)據(jù)必須使用解析字符串的方式來間接得到;
當存儲大數(shù)據(jù)的時候會影響性能。
示例:
input id="HiddenValue" type="hidden" value="Initial Value" runat="server" NAME="HiddenValue">
2.7.2 使用View State
所有的Web Form頁面和控件都包含有一個ViewState屬性,在對同一頁面多次請求時可以保持頁面內(nèi)的值。它的內(nèi)部實現(xiàn)是維護相應的hidden field,只不過是加密了的,所以比hidden field的安全性要好。
使用View State的性能表現(xiàn)很大程度上依賴于服務器控件的類型。一般來說,Label,TextBox,CheckBox,RadioButton,HyperLink的性能要好一些,而DropdownList,ListBox,DataGrid和DataList就要差很多,因為包含的數(shù)據(jù)量太大,所以每次頁面回送都很耗時間。
有些情況下不推薦使用ViewState,比如:
1、不需要回送的頁面避免使用;
2、避免使用ViewState保存大數(shù)據(jù)量;
3、在需要使用會話超時的情況下避免使用它,因為它沒有超時操作。
ViewState的性能表現(xiàn)和Hidden Field的是類似的,但是具有更高的安全性。
優(yōu)點:
數(shù)據(jù)在頁面中自動維護,不需要服務器資源;
實現(xiàn)簡單;
數(shù)據(jù)是經(jīng)過加密和壓縮的,比hidden field有更高的安全性;
數(shù)據(jù)存在客戶端,可以在Web Farm情況下使用。
缺點:
存儲大數(shù)據(jù)量時會降低性能;
和hidden field類似,在客戶端數(shù)據(jù)仍然有潛在的安全威脅。
示例代碼如下:
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"] = "USA";
}
}
…
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();
}
}
2.7.3 使用Hidden Frame
你可以使用Hidden Frame在客戶端緩存數(shù)據(jù),這就避免了使用hidden field和使用view state時每次頁面回送時的緩存數(shù)據(jù)往返。比如你可以秘密的加載多個頁面所需要的圖片,這并不會消耗服務器資源。
優(yōu)點:
a. 可以加載較多數(shù)據(jù)而不只是單個欄位的值;
b. 避免了不必要的多次回送中的數(shù)據(jù)往來;
c.可以緩存和讀取在不同表單中存儲的數(shù)據(jù)項(可以同時緩存多個頁面的數(shù)據(jù));
d.可以訪問同一站點不同frame中的客戶端腳本數(shù)據(jù)。
缺點:
a. 有些瀏覽器不支持frame;
b. 源代碼可以在客戶端看到,有潛在的安全威脅;
c. 隱藏frame的數(shù)量沒有限制,如果框架頁面包含較多hidden frame的話,在首次加載時速度會有限制。
示例代碼如下:
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>
2.7.4 使用Cookies
Cookie是可以在客戶端存儲數(shù)據(jù)另一種方案,這里不過多介紹。
優(yōu)點:
不需要服務器資源;數(shù)據(jù)保存在客戶端,在用戶請求時發(fā)送到服務器上。
使用簡單。Cookie包含簡單的鍵值對,主要保存輕量級的文本數(shù)據(jù)。
支持過期策略;可以指定當會話結束時過期,也可指定一個時間策略。
缺點:
數(shù)據(jù)量的限制;
用戶可能設置為拒絕Cookie;
安全問題;用戶可能更改機器上的cookie信息,造成基于cookie的系統(tǒng)運行失?。?br />
可能過期或被用戶刪除,造成一定程度的不可用。
參看示例代碼:
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;
}
}
2.7.5 使用Query String
Query String是在用戶請求的URL后加上相應的參數(shù)來使用的,只能在使用HTTP GET方式調(diào)用URL時可用。
優(yōu)點:
d.不需要服務器資源,參數(shù)附在URL里面;
e. 應用面廣,幾乎所有瀏覽器都支持;
f.實現(xiàn)簡單,服務端使用Request對象可直接讀取。
缺點:
a.參數(shù)直接對用戶可見,不安全;
b.URL長度的限制,多數(shù)瀏覽器不支持超過255字符的URL。
示例代碼:
string user = Request.QueryString["User"];
2.7.6 小結
下表是使用客戶端緩存的建議:
2.8 使用Internet Explorer緩存
IE提供了緩存機制,可以實現(xiàn)對頁面的數(shù)據(jù)進行緩存,同時可以指定過期時間。用戶在IE中請求此頁面,如果當過期時間沒有到,則自動從緩存中提取并呈現(xiàn);否則,就到服務器上獲取新版本。IE對頁面的緩存可以在IIS中設置。
適合在Internet Explorer中緩存的內(nèi)容
頁面中的圖像文件;
靜態(tài)的文本內(nèi)容;
頁面的標題欄和頁腳內(nèi)容——改變頻率很低,可以給用戶一個迅速相應;
網(wǎng)站的首頁——更改次數(shù)頁時相對較少的;
使用動態(tài)HTML在客戶端保存的特定數(shù)據(jù),比如客戶自定義的顏色和布局設置信息。
優(yōu)點:
減少對服務器的請求和網(wǎng)絡負擔;
支持離線瀏覽;
可以實現(xiàn)基于XML的客戶端復雜應用。
缺點:
客戶端的過期時間必須預先指定而不能依賴于服務器更新;IE采用的是Lazy更新機制,優(yōu)先從緩存中提取數(shù)據(jù);
對其它客戶端瀏覽器沒有作用;
存儲的數(shù)據(jù)沒有加密,不能保證客戶端數(shù)據(jù)安全。
示例代碼:
META HTTP-EQUIV="expires" CONTENT="Tue, 23 Jun 2002 01:46:05 GMT">
本文介紹了緩存和狀態(tài)數(shù)據(jù)存儲的相關概念,以及可供使用的緩存技術,介紹了各種技術的適用范圍,并對其優(yōu)缺點進行了說明,另外有簡單的性能比較和簡單的示例代碼。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助。
您可能感興趣的文章:- asp.net(C#)遍歷memcached緩存對象
- asp.net 使用駐留在頁面中的Cache緩存常用可定時更新的數(shù)據(jù)
- asp.net下Cache 緩存操作類代碼
- asp.net開發(fā)中怎樣去突破文件依賴緩存
- ASP.NET性能優(yōu)化之讓瀏覽器緩存動態(tài)網(wǎng)頁的方法
- asp.net中Session緩存與Cache緩存的區(qū)別分析
- ASP.NET頁面在IE緩存的清除辦法
- 設置ASP.NET頁面不被緩存(客戶端/服務器端取消緩存方法)
- .net/c# memcached緩存獲取所有緩存鍵的方法步驟
- Asp.net禁用頁面緩存的方法總結