系統緩存描述

系統緩存的概述

     有時候總聽到大家說網站運行好慢,不知如何是好;有時候也總見到一些朋友寫的網站功能看起來非常好,但訪問性能卻極其差。如何應對這種情況,充分利用系統緩存則是首要之道。

     系統緩存有什麼好處呢?舉個簡單的例子,你想通過網頁查詢某些數據,而這些數據並非實時變化,或者變化的時間是有期限的。例如查詢一些歷史數據。那麼每個 用戶每次查的數據都是一樣的。如果不設置緩存,ASP.NET也會根據每個用戶的請求重複查詢n次,這就增加了不必要的開銷。所以,可能的情況下儘量使用 緩存,從內存中返回數據的速度始終比去數據庫查的速度快,因而可以大大提供應用程序的性能。畢竟現在內存非常便宜,用空間換取時間效率應該是非常划算的。 尤其是對耗時比較長的、需要建立網絡鏈接的數據庫查詢操作等。

     緩存功能是大型網站設計一個很重要的部分。由數據庫驅動的Web應用程序,如果需要改善其性能,最好的方法是使用緩存功能。

    緩存的分類

     從分佈上來看,我們可以概括爲客戶端緩存和服務器端緩存。如圖所示:

 

                                        圖 緩存的分類

 

客戶端緩存—— 比如你瀏覽一個新的網站,第一次可能要花點時間才能載入整個頁面。而以後再瀏覽時,時間就會大大的縮短,原因就在於這個客戶端緩 存。現在的瀏覽器都比較智能,它會在客戶機器的硬盤上保留許多靜態的文件,比如各種gif,jpeg文件等等。等以後再瀏覽此網頁的時候,它會盡量使用本地緩存裏 面的文件。只有服務器端的文件更新了,或是緩存裏面的文件過期了,它纔會再次從服務器端下載這些東西。很多時候是IE替我們做了這件事情。

服務器端緩存—— 有些東西沒法或是不宜在客戶端緩存,那麼我們只好在服務器端想想辦法了。服務器端緩存從性質上看,又可以分爲兩種。

(1)靜態文件緩存

    好多頁面是靜態的,很少改動,那麼這種文件最適於作靜態緩存。現在的IIS 6.0這部分內容是直接存放在Kernel的內存中,由HTTP.SYS直接管理。由於它在Kernel Space,所以它的性能非常的高。用戶的請求如果在緩存裏面,那麼HTTP.SYS直接將內容發送到network driver上去,不需要像以前那樣從IIS的User space的內存copy到Kernel中,然後再發送到TCP/IP stack上。Kernel level cache幾乎是現在高性能Web server的一個必不可少的特性。

(2)動態緩存

     動態緩存是比較有難度的。因爲你在緩存的時候要時刻注意一個問題,那就是緩存的內容是不是已經過時了。因爲內容過時了可能會有很嚴重的後果。比如網上買賣 股票的網站。你給別人提供的價格是過時的,那人家非砍了你不可。緩存如何發現自己是不是過時就是一個非常複雜的問題。

 

    在ASP.NET中,常見的動態緩存主要有以下幾種手段:

  Ø  傳統緩存方式

  Ø  頁面輸出緩存。

  Ø  頁面局部緩存。

  Ø  利用.NET提供的System.Web.Caching 緩存。

  Ø  緩存依賴。

 

15.4.2  傳統緩存方式

比如將可重複利用的東西放到Application或是Session中去保存。

 Session["Style"] = val;
 Application["Count"] = 0;

===============================================================================

   頁面輸出緩存

      頁面輸出緩存是最爲 簡單的緩存機制,該機制將整個ASP.NET頁面內容保存在服務器內存中。當用戶請求該頁面時,系統從內存中輸出相關數據,直到緩存數據過期。在這個過程 中,緩存內容直接發送給用戶,而不必再次經過頁面處理生命週期。通常情況下,頁面輸出緩存對於那些包含不需要經常修改內容的,但需要大量處理才能編譯完成 的頁面特別有用。需要讀者注意的是,頁面輸出緩存是將頁面全部內容都保存在內存中,並用於完成客戶端請求。

在ASP.NET中頁面緩存的使用方法非常的簡單,只需要在aspx頁的頂部加這樣一句聲明即可:

<%@ OutputCache Duration="60" VaryByParam="none" %>

 

Duration  

緩存的時間(秒)。這是必選屬性。如果未包含該屬性,將出現分析器錯誤。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="CacheWebApp._16_4_3.WebForm1" %>

<%@ OutputCache Duration="60" VaryByParam="none" %>

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>頁面緩存示例</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

    </div>

    </form>

</body>

 

</html>

       

後臺代碼:

       protected void Page_Load(object sender, EventArgs e)

        {

            if (!IsPostBack)

            {

                Label1.Text = DateTime.Now.ToString();

            }

 

        }

       

    如果不加<%@ OutputCache Duration="60" VaryByParam="none" %>,每次刷新頁面上的時間每次都是在變。而加了緩存聲明以後,每次刷新頁面的時間並不變化,60秒後才變化一次,說明數據被緩存了60秒。

VaryByParam

是指頁面根據使用 POST 或 GET 發送的名稱/值對(參數)來更新緩存的內容,多個參數用分號隔開。如果不希望根據任何參數來改變緩存內容,請將值設置爲 none。如果希望通過所有的參數值改變都更新緩存,請將屬性設置爲星號 (*)。

例如: http://localhost:1165/16-4-3/WebForm1.aspx?p=1 
則可以在WebForm1.aspx頁面頭部聲明緩存:<%@ OutputCache Duration="60" VaryByParam="p" %>

以上代碼設置頁面緩存時間是60秒,並根據p參數的值來更新緩存,即p的值發生變化才更新緩存。

如果一直是WebForm1.aspx?p=1訪問該頁,則頁面會緩存當前數據,當p=2時又會執行後臺代碼更新緩存內容。

如果有多個參數時,如:http://localhost:1165/16-4-3/WebForm1.aspx?p=1&n=1

可以這樣聲明:<%@ OutputCache Duration="60" VaryByParam="p;n" %> 

除此之外,@OutputCache 還有一些其他的屬性。@OutputCache指令中的屬性參數描述如下:

<%@ OutputCache Duration="#ofseconds"

   Location="Any | Client | Downstream | Server | None |

     ServerAndClient "

   Shared="True | False"

   VaryByControl="controlname"

   VaryByCustom="browser | customstring"

   VaryByHeader="headers"

   VaryByParam="parametername"

   CacheProfile="cache profile name | ''"

   NoStore="true | false"

   SqlDependency="database/table name pair | CommandNotification"

 

%>

 

 

CacheProfile

用於調用Web.config配置文件中設置的緩存時間。這是可選屬性,默認值爲空字符 ("")。

例如:

在Web.config中加入配置:

<system.web>

    <caching>

        <outputCacheSettings>

            <outputCacheProfiles>

                <addname="CacheTest" duration="50" />

            </outputCacheProfiles>

        </outputCacheSettings>

  

  

</caching>

  

  

</system.web>

  

       

頁面中聲明:

<%@ OutputCache CacheProfile="CacheTest"  VaryByParam="none" %>

注意:

包含在用戶控件(.ascx 文件)中的 @ OutputCache 指令不支持此屬性。在頁中指定此屬性時,屬性值必須與 outputCacheSettings 節下面的 outputCacheProfiles 元素中的一個可用項的名稱匹配。如果此名稱與配置文件項不匹配,將引發異常。

如果每個頁面的緩存時間相同,則不需要每個頁面設置,而是通過統一一個地方控制,這樣就可以更好的統一控制所有頁面的緩存時間。如果想改變緩存時間,只需要改一下web.config的配置信息即可,而不用每個頁面去修改。

 

VaryByControl

通過用戶控件文件中包含的服務器控件來改變緩存(值是控件ID,多控件用分號隔開)。

在 ASP.NET 頁和用戶控件上使用 @ OutputCache 指令時,需要該屬性或 VaryByParam 屬性。

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm2.aspx.cs" Inherits="CacheWebApp._16_4_3.WebForm2" %>

<%@ OutputCache Duration="60" VaryByParam="none" VaryByControl="DropDownList1" %>

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>根據控件頁面緩存</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

         <%=DateTime.Now %>       

        <br>

    <asp:DropDownList ID="DropDownList1" runat="server">

        <asp:ListItem>beijing</asp:ListItem>

        <asp:ListItem>shanghai</asp:ListItem>

        <asp:ListItem>guangzhou</asp:ListItem>

        </asp:DropDownList>

        <asp:Button ID="Button1" runat="server" Text="提交" />

    </div>

    </form>

</body>

 

</html>

 

以上代碼設置緩存有效期是60秒,並且頁面不隨任何GET或POST參數改變(即使不使用VaryByParam屬性, 但是仍然需要在@ OutputControl指令中顯式聲明該屬性)。如果用戶控件中包含ID屬性爲“DropDownList1”的服務器控件(例如下拉框控件),那麼 緩存將根據該控件的變化來更新頁面數據。

 

 

頁面輸出緩存API

Response類的Cache屬性用於獲取頁面緩存策略。該方式的核心是調用 System.Web.HttpCachePolicy。該類主要包含用於設置緩存特定的HTTP標頭的方法和用於控制ASP.NET頁面輸出緩存的方 法。與.NET Framework 1.x中的HttpCachePolicy類相比,.NET Framework 2.0中的HttpCachePolicy類得到了擴充和發展。主要是增加了一些重要方法,例如,SetOmitVarStar方法等。由於 HttpCachePolicy類方法衆多,下面簡要說明幾個常用方法。

SetExpires方法

用於設置緩存過期的絕對時間。它的參數是一個DataTime類的實例,表示過期的絕對時間。

        protected void Page_Load(object sender, EventArgs e)

        {

            // 通過API設置緩存

            //相當於@OutputCache指令中的Duration屬性

            Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));

            Response.Cache.SetExpires(DateTime.Parse("6:00:00PM"));

        }

如上代碼,第一行代碼表示輸出緩存時間是60秒,並且頁面不隨任何GET或POST參數改變,等同於“<%@ OutputCache Duration="60" VaryByParam="none" %>”。第二行代碼設置緩存過期的絕對時間是當日下午6時整。

SetLastModified方法

用於設置頁面的Last-Modified HTTP標頭。Last-Modified HTTP標頭表示頁面上次修改時間,緩存將依靠它來進行計時。如果違反了緩存限制層次結構,此方法將失敗。該方法的參數是一個DataTime類的實例。

 

SetSlidingExpiration方法

該方法將緩存過期從絕對時間設置爲可調時間。其參數是一個布爾值。當參數爲true時,Cache-Control HTTP標頭將隨每個響應而更新。此過期模式與相對於當前時間將過期標頭添加到所有輸出集的IIS配置選項相同。當參數爲False時,將保留該設置,且 任何啓用可調整過期的嘗試都將靜態失敗。此方法不直接映射到HTTP標頭。它由後續模塊或輔助請求來設置源服務器緩存策略。

 

SetOmitVaryStar方法

ASP.NET 2.0新增的方法。用於指定在按參數進行區分時,響應是否應該包含vary:*標頭。方法參數是一個布爾值,若要指示HttpCachePolicy不對其VaryByHeaders屬性使用*值,則爲true;否則爲false。

 

SetCacheability方法

    用於設置頁面的Cache-Control HTTP標頭。該標頭用於控制在網絡上緩存文檔的方式。該方法有兩種重載方式,所不同的是參數。一種重載方法的參數是HttpCacheability枚 舉值,包括NoCache、Private、Public、Server、ServerAndNoCache和ServerAndPrivate(有關這 些枚舉值的定義,可參考MSDN)。另一種方法的參數有兩個,一個參數是HttpCacheability枚舉值,另一個參數是字符串,表示添加到標頭的 緩存控制擴展。需要注意的是,僅當與Private或NoCache指令一起使用時,字段擴展名纔有效。如果組合不兼容的指令和擴展,則此方法將引發無效 參數異常。

===============================================================================

   頁面局部緩存

有時緩存整個頁面是不現實的,因爲頁的某些部分可能在每次請求時都需要變化。在這些情況下,只能緩存頁的一部分。顧名思義,頁面部分緩存是將頁面部分內容保存在內存中以便響應用戶請求,而頁面其他部分內容則爲動態內容。頁面部分緩存的實現包括兩種方式:控件緩存和替換後緩存

 

1. 控件緩存(也稱爲片段緩存)

    這種方式允許將需要緩存的信息包含在一個用戶控件內,然後,將該用戶控件標記爲可緩存的,以此來緩存頁面輸出的部分內容。該選項允許緩存頁面中的特定內 容,而沒有緩存整個頁面,因此,每次都需重新創建整個頁。例如,如果要創建一個顯示大量動態內容(如股票信息)的頁,其中有些部分爲靜態內容(如每週總 結),這時可以將靜態部分放在用戶控件中,並允許緩存這些內容。

    在ASP.NET中,提供了UserControl這種用戶控件的功能。一個頁面可以通過多個UserControl來組成。只需要在某個或某幾個UserControl裏設置緩存。

    例如:

    那麼可以在WebUserControl1.ascx的頁頭代碼中添加聲明語句:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="CacheWebApp._16_4_5.WebUserControl1" %>

<%@ OutputCache Duration="60" VaryByParam="none" %>

 

<%=DateTime.Now %>  

 

     調用該控件的頁面WebForm1.aspx代碼:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="CacheWebApp._16_4_5.WebForm1" %>

<%@ Register src="WebUserControl1.ascx" tagname="WebUserControl1" tagprefix="uc1" %>

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>控件緩存</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

    頁面的:<%=DateTime.Now %>    

    </div>

    <div>

    控件的:<uc1:WebUserControl1 ID="WebUserControl11" runat="server" />

    </div>

    </form>

</body>

 

</html>

     這時候刷新WebForm1.aspx頁面時,頁面的時間每次刷新都變化,而用戶控件中的時間數據卻是60秒才變化一次,說明對頁面的“局部”控件實現了緩存,而整個頁面不受影響。

2. 緩存後替換

     與控件緩存正好相反。它對整個頁面進行緩存,但是頁中的某些片段是動態的,因此不會緩存這些片段。ASP.NET頁面中既包含靜態內容,又包含基於數據庫 數據的動態內容。靜態內容通常不會發生變化。因此,對靜態內容實現數據緩存是非常必要的。然而,那些基於數據的動態內容,則不同。數據庫中的數據可能每時 每刻都發生變化,因此,如果對動態內容也實現緩存,可能造成數據不能及時更新的問題。對此問題如果使用前文所述的控件緩存方法,顯然不切實際,而且實現起 來很繁瑣,易於發生錯誤。

     如何實現緩存頁面的大部分內容,而不緩存頁面中的局部某些片段。ASP.NET 2.0提供了緩存後替換功能。實現該項功能可通過以下三種方法:

    一是以聲明方式使用Substitution控件,

    二是以編程方式使用Substitution控件API,

    三是以隱式方式使用控件。

    前兩種方法的核心是Substitution控件,本節將重點介紹該控件,第三種方法僅專注於控件內置支持的緩存後替換功能,本節僅做簡要說明。

 

(1) Substitution控件應用

     爲提高應用程序性能,可能會緩存整個ASP.NET頁面,同時,可能需要根據每個請求來更新頁面上特定的部分。例如,可能要緩存頁面的很大一部分,需要動 態更新該頁上與時間或者用戶高度相關的信息。在這種情況下,推薦使用Substitution控件。Substitution控件能夠指定頁面輸出緩存中 需要以動態內容替換該控件的部分,即允許對整頁面進行輸出緩存,然後,使用Substitution控件指定頁中免於緩存的部分。需要緩存的區域只執行一次,然後從緩存讀取,直至該緩存項到期或被清除。動態區域,也就是Substitution控件指定的部分,在每次請求頁面時都執行。Substitution控件提供了一種緩存部分頁面的簡化解決方案。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm2.aspx.cs" Inherits="CacheWebApp._16_4_5.WebForm2" %>

<%@ OutputCache Duration="60" VaryByParam="none" %>

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>緩存後替換示例</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

    頁面緩存的時間:<%= DateTime.Now.ToString() %>  

    </div>

    <div>   

       真實(替換)的時間:<asp:Substitution ID="Substitution1" runat="server" MethodName="getCurrentTime" />

    </div>

    </form>

</body>

 

</html>

    頁面後臺代碼:

public partial class WebForm2 : System.Web.UI.Page

{

    public static string getCurrentTime(HttpContext context)

    {

        return DateTime.Now.ToString();

    }

}

    如上代碼所示,Substitution控件有一個重要屬性:MethodName。該屬性用於獲取或者設置當Substitution控件執行時爲回調而調用的方法名稱。該方法比較特殊,必須符合以下3條標準:

  • 此方法必須被定義爲靜態方法;
  • 此方法必須接受HttpContext類型的參數;
  • 此方法必須返回String類型的值。

 

     在運行情況下,Substitution控件將自動調用MethodName屬性所定義的方法。該方法返回的字符串即爲要在頁面中的Substitution控件的位置上顯示的內容。 如果頁面設置了緩存全部輸出,那麼在第一次請求時,該頁將運行並緩存其輸出。對於後續的請求,將通過緩存來完成,該頁上的其他代碼不會再運行。但 Substitution控件及其有關方法則在每次請求時都執行,並且自動更新該控件所表示的動態內容,這樣就實現了整體緩存,局部變化的替換效果。

 

     如上代碼所示,在代碼頭部通過@ OutputCache指令設置頁面輸出緩存過期時間爲5秒,這意味着整個頁面數據都應用了緩存功能。因此,“頁面緩存的時間”所顯示的時間值來自於數據 緩存。這個時間值不會隨着刷新頁面而變化,僅當緩存過期時纔會發生更新。Substitution控件的MethodName屬性值爲 getCurrentTime。該控件顯示的內容來自於getCurrentTime方法的返回值。尤爲重要的是,雖然頁面設置了輸出緩存功能,但是每當 頁面刷新時,ASP.NET執行引擎仍然要重新執行Substitution控件,並將MethodName屬性值指定的方法返回值顯示在頁面上,因此, 顯示的是當前最新時間。

示例效果,如圖15-2所示:

 

圖15-2  緩存後替換

隨着頁面的刷新,真實時間在變,而頁面緩存的時間在指定的緩存時間內始終不變。

 

注意:

l  Substitution控件無法訪問頁上的其他控件,也就是說,無法檢查或更改其他控件的值。但是,代碼確實可以使用傳遞給它的參數來訪問當前頁上下文。

l  在緩存頁包含的用戶控件中可以包含Substitution控件。但是,在輸出緩存用戶控件中不能放置Substitution控件。

l  Substitution控件不會呈現任何標記,其位置所顯示內容完全取決於所定義方法的返回字符串。

 

(2) Substitution控件API應用

上一小節介紹了以聲明方式使用Substitution控件實現緩存後替換的應用。本節說明另一種實現方法。該方法的核心是以編程方式利用Substitution控件API實現緩存後替換,相對於以聲明方式使用Substitution控件的方法具有更強靈活性。

通過爲Substitution指定回調方法,實現和聲明同樣的效果。Substitution的回調方法必須是

HttpResponseSubstitutionCallback委託定義的方法,它有兩個特徵:

l  一是返回值必須是String,

l  二是參數有且僅有一個,並且是HttpContext類型。

當需要以編程方式,爲緩存的輸出響應動態生成指定的響應區域時,可以在頁面代碼中將某個方法(即回調方法)的名稱作爲參 數(HttpResponseSubstitutionCallback)傳遞給Substitution。這樣Substitution就能夠使用回調 方法,並將回調方法的返回值作爲給定位置的替代內容顯示出來。

需要注意的是,回調方法必須是線程安全的,可以是作爲容器的頁面或者用戶控件中的靜態方法,也可以是其他任意對象上的靜態方法或實例方法。

 

下面演示一個以編程方式將 Substitution 控件添加到輸出緩存網頁。與(1)Substitution控件應用所示的示例完成同樣功能。不同的是實現方式。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm3.aspx.cs" Inherits="CacheWebApp._16_4_5.WebForm3" %>

<%@ OutputCache Duration="60" VaryByParam="none" %>

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title>緩存後替換-Substitution控件API應用</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        頁面緩存的時間:<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

    </div>

    <div>

        真實(緩存替換)的時間:

        <asp:PlaceHolder ID="PlaceHolder1" runat="Server"></asp:PlaceHolder>

    </div>

    </form>

</body>

 

</html>

    頁面後臺CS代碼:  

protected void Page_Load(object sender, EventArgs e)

{

    //創建一個Substitution

    Substitution Substitution1 = new Substitution();

    //指定調用的回調方法名

    Substitution1.MethodName = "GetCurrentDateTime";           

    PlaceHolder1.Controls.Add(Substitution1);       

 

    Label1.Text=DateTime.Now.ToString();   

}

public static string GetCurrentDateTime(HttpContext context)

{

    return DateTime.Now.ToString();

}

    如上代碼所示,頁面使用@ OutputCache指令設置了輸出緩存功能,其配置數據緩存過期時間爲60秒。然而,頁面其他內容都被緩存,通過Substitution調用的回調方法顯示的內容是不被緩存的。

===============================================================================

應用程序數據緩存

System.Web.Caching 命名空間提供用於緩存服務器上常用數據的類。此命名空間包括 Cache 類,該類是一個字典,您可以在其中存儲任意數據對象,如哈希表和數據集。它還爲這些對象提供了失效功能,併爲您提供了添加和移除這些對象的方法。您還可以 添加依賴於其他文件或緩存項的對象,並在從 Cache 對象中移除對象時執行回調以通知應用程序。

 

/// <summary>

/// 獲取當前應用程序指定CacheKey的Cache對象值

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <returns>返回緩存對象</returns>

public static object GetCache(string CacheKey)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    return objCache[CacheKey];

}

/// <summary>

/// 設置當前應用程序指定CacheKey的Cache對象值

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <param name="objObject">緩存對象</param>

public static void SetCache(string CacheKey, object objObject)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    objCache.Insert(CacheKey, objObject);

}

/// <summary>

/// 設置當前應用程序指定CacheKey的Cache對象值

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <param name="objObject">緩存對象</param>

/// <param name="absoluteExpiration">絕對過期時間</param>

/// <param name="slidingExpiration">最後一次訪問所插入對象時與該對象過期時之間的時間間隔</param>

public static void SetCache(string CacheKey, object objObject, DateTime absoluteExpiration, TimeSpan slidingExpiration)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    objCache.Insert(CacheKey, objObject, null, absoluteExpiration, slidingExpiration);

}       

protected void Page_Load(object sender, EventArgs e)

{

    string CacheKey = "cachetest";

    object objModel = GetCache(CacheKey);//從緩存中獲取

    if (objModel == null)//緩存裏沒有

    {

        objModel = DateTime.Now;//把當前時間進行緩存

        if (objModel != null)

        {

            int CacheTime = 30;//緩存時間30秒

            SetCache(CacheKey, objModel, DateTime.Now.AddSeconds(CacheTime), TimeSpan.Zero);//寫入緩存

        }

    }

    Label1.Text = objModel.ToString();

}

 

    以上幾種方法都很好的解決了數據緩存的問題,但由一個最大的問題是當數據發生變化了,而緩存裏還是過期的數據,只有等緩存過期後纔會重新獲取最新的數據, 這樣的話,很多時候用戶獲取的數據都是和實際數據不一致的過期數據。這同樣給用戶造成了比較大的麻煩,怎麼辦呢?接着往下看。

===============================================================================

 文件緩存依賴

    這種策略讓緩存依賴於一個指定的文件,通過改變文件的更新日期來清除緩存。

                                               

/// <summary>

/// 獲取當前應用程序指定CacheKey的Cache對象值

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <returns>返回緩存對象</returns>

public static object GetCache(string CacheKey)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    return objCache[CacheKey];

}

/// <summary>

/// 設置以緩存依賴的方式緩存數據

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <param name="objObject">緩存對象</param>

/// <param name="cacheDepen">依賴對象</param>

public static void SetCache(string CacheKey, object objObject, System.Web.Caching.CacheDependency dep)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    objCache.Insert(

        CacheKey,

        objObject,

        dep,

        System.Web.Caching.Cache.NoAbsoluteExpiration, //從不過期

        System.Web.Caching.Cache.NoSlidingExpiration, //禁用可調過期

        System.Web.Caching.CacheItemPriority.Default,

        null);

}

protected void Page_Load(object sender, EventArgs e)

{

    string CacheKey = "cachetest";

    object objModel = GetCache(CacheKey);//從緩存中獲取

    if (objModel == null) //緩存裏沒有

    {

        objModel = DateTime.Now;//把當前時間進行緩存

        if (objModel != null)

        {

            //依賴 C:\\test.txt 文件的變化來更新緩存

            System.Web.Caching.CacheDependency dep = new System.Web.Caching.CacheDependency("C:\\test.txt");

            SetCache(CacheKey, objModel, dep);//寫入緩存

        }

    }

 

    Label1.Text = objModel.ToString();

}

    當我們改變test.txt的內容時,緩存會自動更新。這種方式非常適合讀取配置文件的緩存處理。如果配置文件不變化,就一直讀取緩存的信息,一旦配置發生變化,自動更新同步緩存的數據。

這種方式的缺點是,如果緩存的數據比較多,相關的依賴文件比較鬆散,對管理這些依賴文件有一定的麻煩。對於負載均衡環境下,還需要同時更新多臺Web服務器下的緩存文件,如果多個Web應用中的緩存依賴於同一個共享的文件,可能會省掉這個麻煩。 

===============================================================================

數據庫緩存依賴

更多的時候,我們的服務器性能損耗還是在查詢數據庫的時候,所以對數據庫的緩存還是顯得特別重要,上面幾種方式都可以實現部分數據緩存功能。但問題是我們 的數據有時候是在變化的,這樣用戶可能在緩存期間查詢的數據就是老的數據,從而導致數據的不一致。那有沒有辦法做到,數據如果不變化,用戶就一直從緩存中 取數據,一旦數據變化,系統能自動更新緩存中的數據,從而讓用戶得到更好的用戶體驗。

   答案是肯定的!.NET已經爲我們提供了這樣一種非常好的解決方法:SqlCacheDependency數據庫緩存依賴。

  

實現步驟:

    下面就讓我們看一下如何實現數據庫緩存依賴功能:

第一步: 修改web.config,讓項目啓用SqlCacheDependency 。

將下列代碼加入web.config的<system.web>節:        

<?xml version="1.0"?>

<configuration>

    <appSettings/>

    <connectionStrings>

        <addname="strcodematic" connectionString="data source=127.0.0.1;initial catalog=codematic;user id=sa;password="  providerName="System.Data.SqlClient" />

    </connectionStrings>

    <system.web>

        <caching>

            <sqlCacheDependencyenabled="true" pollTime="6000">

                <databases>

                    <addname="codematic" connectionStringName="strcodematic" />

                </databases>

            </sqlCacheDependency>         

        </caching>

    

        <compilation debug="true">

        </compilation>     

        <authentication mode="Windows"/>      

    </system.web>

</configuration>

 

這裏的connectionStringName指定了在<connectionStrings>中添加的某一個連接字符串。name則是爲該SqlCacheDependency起的名字,這個名字將在第3步中用到。
SqlCacheDependency類會自動完成對此配置節信息的讀取以建立和數據庫之間的聯繫。

注意:

在<databases>節的<add name="codematic" connectionStringName="strcodematic" />中的name屬性值必須和第三步的Page_Load代碼中System.Web.Caching.SqlCacheDependency("codematic", "P_Product");中的第一個參數(數據庫名稱)相一致。


第二步:執行下述命令,爲 數據庫啓用緩存依賴。

如果要配置SqlCacheDependency,則需要以命令行的方式執行。

aspnet_regsql.exe工具位於Windows\\Microsoft.NET\\Framework\\[版本]文件夾中。

aspnet_regsql -C "data source=127.0.0.1;initial catalog=codematic;user id=sa;password=" -ed -et -t "P_Product"

參數-C後面的字符串是連接字符串(請替換成自己所需要的值),

參數-t後面的字符串是數據表的名字。

 運行結果如圖15-3所示:

 

  

 

圖15-3  啓用數據庫緩存依賴

 命令執行後,在指定的數據庫中會多出一個AspNet_SqlCacheTablesForChangeNotification表。
 

注意:

要使得7.0或者2000版本以上的SQLServer支持SqlCacheDependency特性,需要對數據庫服務器執行相關的配置。

有兩種方法配置SQLServer:

一 使用aspnet_regsql命令行工具,

二 使用SqlCacheDependencyAdmin類。

例如:

aspnet_regsql -S "server" -E -d "database" –ed  或者

aspnet_regsql -S "server" -E -d "database" -et -t "table"
如果是Sql驗證的話要把-E換成,-U (用戶名),-P (密碼)

以下是該工具的命令參數說明

-? 顯示該工具的幫助功能;

-S 後接的參數爲數據庫服務器的名稱或者IP地址;

-U 後接的參數爲數據庫的登陸用戶名;

-P 後接的參數爲數據庫的登陸密碼;

-E 使用當前登錄用戶的 Windows 集成認證進行身份驗證。

-d 後接參數爲對哪一個數據庫採用SqlCacheDependency功能;

-C 連接數據庫的連接字符串。如果您指定服務器(-S)和登錄(-U和-P,或 -E)信息,則此選項不是必需的,因爲連接字符串已經包含這些信息。

-t 後接參數爲對哪一個表採用SqlCacheDependency功能;

-ed 允許對數據庫使用SqlCacheDependency功能;

-dd 禁止對數據庫採用SqlCacheDependency功能;

-et 允許對數據表採用SqlCacheDependency功能;

-dt 禁止對數據表採用SqlCacheDependency功能;

-lt 列出當前數據庫中有哪些表已經採用sqlcachedependency功能。

 


第三步:在代碼中使用緩存,併爲其設置SqlCacheDependency依賴:

   

/// <summary>

/// 獲取當前應用程序指定CacheKey的Cache對象值

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <returns>返回緩存對象</returns>

public static object GetCache(string CacheKey)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    return objCache[CacheKey];

}

/// <summary>

/// 設置以緩存依賴的方式緩存數據

/// </summary>

/// <param name="CacheKey">索引鍵值</param>

/// <param name="objObject">緩存對象</param>

/// <param name="cacheDepen">依賴對象</param>

public static void SetCache(string CacheKey, object objObject, System.Web.Caching.CacheDependency dep)

{

    System.Web.Caching.Cache objCache = HttpRuntime.Cache;

    objCache.Insert(

        CacheKey,

        objObject,

        dep,

        System.Web.Caching.Cache.NoAbsoluteExpiration,//從不過期

        System.Web.Caching.Cache.NoSlidingExpiration,//禁用可調過期

        System.Web.Caching.CacheItemPriority.Default,

        null);

}

protected void Page_Load(object sender, EventArgs e)

{

    string CacheKey = "cachetest";

    object objModel = GetCache(CacheKey);//從緩存中獲取

    if (objModel == null)//緩存裏沒有

    {

        objModel = GetData();//把當前時間進行緩存

        if (objModel != null)

        {

            //依賴數據庫codematic中的P_Product表變化 來更新緩存

            System.Web.Caching.SqlCacheDependency dep = new System.Web.Caching.SqlCacheDependency("codematic", "P_Product");

            SetCache(CacheKey, objModel, dep);//寫入緩存

        }

    }

               

    GridView1.DataSource = (DataSet)objModel;

    GridView1.DataBind();

}

 

//查詢數據

private DataSet GetData()

{

    string conString = "data source=127.0.0.1;initial catalog=codematic;user id=sa;password=";

    string strSQL = "SELECT * FROM P_Product";

    SqlConnection myConnection = new SqlConnection(conString);

    DataSet ds = new DataSet();

    myConnection.Open();

    SqlDataAdapter adapter = new SqlDataAdapter(strSQL, myConnection);

    adapter.Fill(ds, "Product");

    myConnection.Close();

    return ds;

}

 

     從以上代碼可以看出,和文件依賴基本相同,只是在存放緩存SetCache時存入的依賴對象不同罷了。這裏用的是SqlCacheDependency。

     其中,創建SqlCacheDependency的構造方法:

public SqlCacheDependency (string databaseEntryName,string tableName)

 

l  databaseEntryName :是在Web.config 文件的 caching 節的 sqlCacheDependency 的 databases 元素中定義的數據庫的名稱。

l  tableName :與 SqlCacheDependency 關聯的數據庫表的名稱。

    這樣,只有當P_Product表的內容發生變化時,查詢操作纔會重新查詢數據更新緩存的內容,可以大大減少數據庫的重複查詢和提高系統的性能和運行效率。

 

===============================================================================

第三方分佈式緩存解決方案 Memcached和Cacheman

Memcached — 分佈式緩存系統

1.Memcached是什麼?

    Memcached是高性能的,分佈式的內存對象緩存系統,用於在動態應用中減少數據庫負載,提升訪問速度。Memcached通過在內存裏維護一個統一 的巨大的hash表,它能夠用來存儲各種格式的數據,包括圖像、視頻、文件以及數據庫檢索的結果等。Memcached由Danga Interactive最初爲了加速 LiveJournal網站訪問速度而開發的,後來被很多大型的網站採用。起初作者編寫它可能是爲了提高動態網頁應用,爲了減輕數據庫檢索的壓力,來做的 這個緩存系統。它的緩存是一種分佈式的,也就是可以允許不同主機上的多個用戶同時訪問這個緩存系統,這種方法不僅解決了共享內存只能是單機的弊端, 同時也解決了數據庫檢索的壓力,最大的優點是提高了訪問獲取數據的速度!基於memcached作者對分佈式cache的理解和解決方案。 memcached完全可以用到其他地方 比如分佈式數據庫,分佈式計算等領域。Memcached將數據庫負載大幅度降低,更好的分配資源,更快速訪問。

2.Memcached工作機制

    通過在內存中開闢一塊區域來維持一個大的hash表來加快頁面訪問速度,和數據庫是獨立的。但是目前主要用來緩存數據庫的數據。允許多個server通過 網絡形成一個大的hash,用戶不必關心數據存放在哪,只調用相關接口就可。存放在內存的數據通過LRU算法進行淘汰出內存。同時可以通過刪除和設置失效 時間來淘汰存放在內存的數據。

    現在一些.NET開發人員開始放棄ASP.NET內置的緩存機制,轉而使用Memcached——一種分佈式的內存緩存系統。當運行在單獨的Web服務器 上,你可以很容易地清除一個已經確認被改變了的緩存。可惜,ASP.NET沒有一個很好的方法來支持多服務器。每個服務器上的緩存都對其他緩存的改變一無 所知。

    ASP.NET允許通過基於文件系統和數據庫表的觸發器來作廢一個緩存。然而,這也存在問題,比如數據庫觸發器需要使用昂貴的輪詢,以及觸發器本身冗長的編程。但是,我們還是有其他的選擇的。

    不像ASP.NET內置的緩存機制,Memcached是一個分佈式的緩存系統。任何Web服務器都能更新或刪除一個緩存項,並且所有其他的服務器都能在 下次訪問這些緩存項的時候自動獲取到更新的內容。這是通過把這些緩存項存儲在一個或者多個緩存服務器上來實現的。每一個緩存項都根據它的關鍵字的哈希值來 分配到一個服務器上。

    表面看來,Memcached針對ASP.NET的API就像和內置的API一樣。這讓開發人員很容易地轉換到Memcached上,僅僅通過在代碼中查找和替換即可實現。

    一個被推薦的解決方案是不根據緩存項的關鍵字來生成哈希鍵值。這將允許開發人員能夠讓一個給定頁面中需要的所有緩存項,儘量存放在同一個服務器上。可惜, 基於數據保存的地方而不是基於緩存項自身的關鍵字來生成哈希鍵,很容易產生錯誤,需要仔細來實現(這個算法)。

     Memcached是基於Linux運行的,你可以在BSD的許可協議下使用Memcached。他也提供了針對C#的客戶端以及Perl、Python、PHP、Java和其他語言的API:http://www.danga.com/memcached/apis.bml。還有一個Win32的移植版本(http://jehiah.cz/projects/memcached-win32/),可以讓Memcached運行在非Linux的機器上。

 

     Cacheman — .NET架構下的分佈式緩存項目

      Cacheman據說是由微軟旗下的 Popfly 項目組成員 Sriram Krishnan 的作品。是他用業餘時間開發的。最新的情況是,微軟的 Popfly 網站已經“悄悄地”的做了更新,就是採用了 Krishnan 的 Cacheman,更新了緩存機制。該項緩存技術更新帶來的性能提升非常顯著,根據Popfly團隊中的 John Montgomery 的說法:加載一個已有的Mashup應用時,可以帶來2到6倍的性能提升。

       這些說法也得到了 Krishnan 本人的確認。他提到這是Cacheman 的第一次的實際應用,並自豪的說 Cacheman 不費吹灰之力就拿下了 Popfly 的全部訪問量。

     簡單介紹一下 Cacheman 這個項目。資料主要來源於 Krishnan的博客對Cacheman的介紹。

Cacheman是一個基於Windows平臺的快速分佈式哈希表。是由純託管代碼實現。中間擱置了有幾個月,直到最近纔開始重新上馬這個項目,極可能就是因爲Popfly項目需要的緣故纔開始着手的。

     Krishnan本人對 memcached 很感興趣,於是創建了 Cacheman。Cacheman上有很多 memcached 的影子,比如與memcached相似的文本通訊協議。Cacheman的通訊協議公開,任何人可以根據自己偏愛的語言環境寫客戶端。 Krishnan 在自己家用電腦(2.4GHz Intel Core 2 帶2GB內存)上進入測試,達到了每秒16000次左右的請求,並且還是服務器與客戶端都是在同一臺服務器下完成的。

    現這款產品還不太完善,作者自身也提到:在Cacheman做指定key的GET/SET/DELETE操作 時,客戶端需要弄清需要與哪一臺Cacheman服務器通訊,爲此要對該key做一個快速FNV哈希然後求餘得到應該和幾臺服務器中的哪臺服務器通訊。但 該法的缺點在於新增或刪除一個服務器節點時,緩存節點需要大規模遷移。修復該問題需要一致性的哈希算法,作者表示還沒有時間解決此事。作者提出了採用中心 架構的“主緩存服務器”的解決辦法,讓客戶端輪詢主緩存服務器來獲取應該與那個緩存服務器通訊,但他也覺的這樣做增加了複雜性,會帶來些新問題。

    可以感覺到,由於 Cacheman 這個個人項目已經介入到 Popfly 這個正式產品中,可能很快就會被微軟吸納爲正式產品,因此如果有人採用這個產品做自己緩存的解決方案的話,應該不必太擔心後續的產品升級及文檔支持服務, 它的未來前途值的期待。說不定 Krishnan 會從 Popfly 項目脫身出來專職負責這個 Cacheman 項目。

發佈了45 篇原創文章 · 獲贊 10 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章