PetShop之ASP.NET緩存設計
PetShop之ASP.NET緩存
如果對微型計算機硬件系統有足夠的了解,那么我們對于Cache這個名詞一定是耳熟能詳的。在CPU以及主板的芯片中,都引入了這種名為高速緩沖存儲器(Cache)的技術。因為Cache的存取速度比內存快,因而引入Cache能夠有效的解決CPU與內存之間的速度不匹配問題。硬件系統可以利用Cache存儲CPU訪問概率高的那些數據,當CPU需要訪問這些數據時,可以直接從Cache中讀取,而不必訪問存取速度相對較慢的內存,從而提高了CPU的工作效率。軟件設計借鑒了硬件設計中引入緩存的機制以改善整個系統的性能,尤其是對于一個數據庫驅動的Web應用程序而言,緩存的利用是不可或缺的,畢竟,數據庫查詢可能是整個Web站點中調用最頻繁但同時又是執行最緩慢的操作之一,我們不能被它老邁的雙腿拖緩我們前進的征程。緩存機制正是解決這一缺陷的加速器。
4.1 ASP.NET緩存概述
作為.Net框架下開發Web應用程序的主打產品,ASP.NET充分考慮了緩存機制。通過某種方法,將系統需要的數據對象、Web頁面存儲在內存中,使得Web站點在需要獲取這些數據時,不需要經過繁瑣的數據庫連接、查詢和復雜的邏輯運算,就可以“觸手可及”,如“探囊取物”般容易而快速,從而提高整個Web系統的性能。
ASP.NET提供了兩種基本的緩存機制來提供緩存功能。一種是應用程序緩存,它允許開發者將程序生成的數據或報表業務對象放入緩存中。另外一種緩存機制是頁輸出緩存,利用它,可以直接獲取存放在緩存中的頁面,而不需要經過繁雜的對該頁面的再次處理。
應用程序緩存其實現原理說來平淡無奇,僅僅是通過ASP.NET管理內存中的緩存空間。放入緩存中的應用程序數據對象,以鍵/值對的方式存儲,這便于用戶在訪問緩存中的數據項時,可以根據key值判斷該項是否存在緩存中。
放入在緩存中的數據對象其生命周期是受到限制的,即使在整個應用程序的生命周期里,也不能保證該數據對象一直有效。ASP.NET可以對應用程序緩存進行管理,例如當數據項無效、過期或內存不足時移除它們。此外,調用者還可以通過CacheItemRemovedCallback委托,定義回調方法使得數據項被移除時能夠通知用戶。
在.Net Framework中,應用程序緩存通過System.Web.Caching.Cache類實現。它是一個密封類,不能被繼承。對于每一個應用程序域,都要創建一個Cache類的實例,其生命周期與應用程序域的生命周期保持一致。我們可以利用Add或Insert方法,將數據項添加到應用程序緩存中,如下所示:
Cache.Insert("Second", "Second Item");
我們還可以為應用程序緩存添加依賴項,使得依賴項發生更改時,該數據項能夠從緩存中移除:
Cache.Insert("Third", "Third Item",
new System.Web.Caching.CacheDependency(null, dependencies));
與之對應的是緩存中數據項的移除。前面提到ASP.NET可以自動管理緩存中項的移除,但我們也可以通過代碼編寫的方式顯式的移除相關的數據項:
相對于應用程序緩存而言,頁輸出緩存的應用更為廣泛。它可以通過內存將處理后的ASP.NET頁面存儲起來,當客戶端再一次訪問該頁面時,可以省去頁面處理的過程,從而提高頁面訪問的性能,以及Web服務器的吞吐量。例如,在一個電子商務網站里,用戶需要經常查詢商品信息,這個過程會涉及到數據庫訪問以及搜索條件的匹配,在數據量較大的情況下,如此的搜索過程是較為耗時的。此時,利用頁輸出緩存就可以將第一次搜索得到的查詢結果頁存儲在緩存中。當用戶第二次查詢時,就可以省去數據查詢的過程,減少頁面的響應時間。
頁輸出緩存分為整頁緩存和部分頁緩存。我們可以通過@OutputCache指令完成對Web頁面的輸出緩存。它主要包含兩個參數:Duration和VaryByParam。Duration參數用于設置頁面或控件進行緩存的時間,其單位為秒。如下的設置表示緩存在60秒內有效:
只要沒有超過Duration設置的期限值,當用戶訪問相同的頁面或控件時,就可以直接在緩存中獲取。
使用VaryByParam參數可以根據設置的參數值建立不同的緩存。例如在一個輸出天氣預報結果的頁面中,如果需要為一個ID為txtCity的TextBox控件建立緩存,其值將顯示某城市的氣溫,那么我們可以進行如下的設置:
如此一來,ASP.NET會對txtCity控件的值進行判斷,只有輸入的值與緩存值相同,才從緩存中取出相應的值。這就有效地避免了因為值的不同而導致輸出錯誤的數據。
利用緩存的機制對性能的提升非常明顯。通過ACT(Application Center Test)的測試,可以發現設置緩存后執行的性能比未設置緩存時的性能足足提高三倍多。
引入緩存看來是提高性能的“完美”解決方案,然而“金無足赤,人無完人”,緩存機制也有缺點,那就是數據過期的問題。一旦應用程序數據或者頁面結果值發生的改變,那么在緩存有效期范圍內,你所獲得的結果將是過期的、不準確的數據。我們可以想一想股票系統利用緩存所帶來的災難,當你利用錯誤過期的數據去分析股市的風云變幻時,你會發現獲得的結果真可以說是“失之毫厘,謬以千里”,看似大好的局面就會像美麗的泡沫一樣,用針一戳,轉眼就消失得無影無蹤。
那么我們是否應該為了追求高性能,而不顧所謂“數據過期”所帶來的隱患呢?顯然,在類似于股票系統這種數據更新頻繁的特定場景下,數據過期的糟糕表現甚至比低效的性能更讓人難以接受。故而,我們需要在性能與數據正確性間作出權衡。所幸的是,.Net Framework 2.0引入了一種新的緩存機制,它為我們的“魚與熊掌兼得”帶來了技術上的可行性。
.Net 2.0引入的自定義緩存依賴項,特別是基于MS-SQL Server的SqlCacheDependency特性,使得我們可以避免“數據過期”的問題,它能夠根據數據庫中相應數據的變化,通知緩存,并移除那些過期的數據。事實上,在PetShop 4.0中,就充分地利用了SqlCacheDependency特性。
4.2 SqlCacheDependency特性
SqlCacheDependency特性實際上是通過System.Web.Caching.SqlCacheDependency類來體現的。通過該類,可以在所有支持的SQL Server版本(7.0,2000,2005)上監視特定的SQL Server數據庫表,并創建依賴于該表以及表中數據行的緩存項。當數據表或表中特定行的數據發生更改時,具有依賴項的數據項就會失效,并自動從Cache中刪除該項,從而保證了緩存中不再保留過期的數據。
由于版本的原因,SQL Server 2005完全支持SqlCacheDependency特性,但對于SQL Server 7.0和SQL Server 2000而言,就沒有如此幸運了。畢竟這些產品出現在.Net Framework 2.0之前,因此它并沒有實現自動監視數據表數據變化,通知ASP.NET的功能。解決的辦法就是利用輪詢機制,通過ASP.NET進程內的一個線程以指定的時間間隔輪詢SQL Server數據庫,以跟蹤數據的變化情況。
要使得7.0或者2000版本的SQL Server支持SqlCacheDependency特性,需要對數據庫服務器執行相關的配置步驟。有兩種方法配置SQL Server:使用aspnet_regsql命令行工具,或者使用SqlCacheDependencyAdmin類。
4.2.1 利用aspnet_regsql工具
aspnet_regsql工具位于Windows\Microsoft.NET\Framework\[版本]文件夾中。如果直接雙擊該工具的執行文件,會彈出一個向導對話框,提示我們完成相應的操作:
圖4-1 aspnet_regsql工具
如圖4-1所示中的提示信息,說明該向導主要用于配置SQL Server數據庫,如membership,profiles等信息,如果要配置SqlCacheDependency,則需要以命令行的方式執行。以PetShop 4.0為例,數據庫名為MSPetShop4,則命令為:
aspnet_regsql -S localhost -E -d MSPetShop4 -ed
以下是該工具的命令參數說明:
-? 顯示該工具的幫助功能;
-S 后接的參數為數據庫服務器的名稱或者IP地址;
-U 后接的參數為數據庫的登陸用戶名;
-P 后接的參數為數據庫的登陸密碼;
-E 當使用windows集成驗證時,使用該功能;
-d 后接參數為對哪一個數據庫采用SqlCacheDependency功能;
-t 后接參數為對哪一個表采用SqlCacheDependency功能;
-ed 允許對數據庫使用SqlCacheDependency功能;
-dd 禁止對數據庫采用SqlCacheDependency功能;
-et 允許對數據表采用SqlCacheDependency功能;
-dt 禁止對數據表采用SqlCacheDependency功能;
-lt 列出當前數據庫中有哪些表已經采用sqlcachedependency功能。
以上面的命令為例,說明將對名為MSPetShop4的數據庫采用SqlCacheDependency功能,且SQL Server采用了windows集成驗證方式。我們還可以對相關的數據表執行aspnet_regsql命令,如:
aspnet_regsql -S localhost -E -d MSPetShop4 -t Item -et
aspnet_regsql -S localhost -E -d MSPetShop4 -t Product -et
aspnet_regsql -S localhost -E -d MSPetShop4 -t Category -et
當執行上述的四條命令后,aspnet_regsql工具會在MSPetShop4數據庫中建立一個名為AspNet_SqlCacheTablesForChangeNotification的新數據庫表。該數據表包含三個字段。字段tableName記錄要追蹤的數據表的名稱,例如在PetShop 4.0中,要記錄的數據表就包括Category、Item和Product。notificationCreated字段記錄開始追蹤的時間。changeId作為一個類型為int的字段,用于記錄數據表數據發生變化的次數。如圖4-2所示:
圖4-2 AspNet_SqlCacheTablesForChangeNotification數據表
除此之外,執行該命令還會為MSPetShop4數據庫添加一組存儲過程,為ASP.NET提供查詢追蹤的數據表的情況,同時還將為使用了SqlCacheDependency的表添加觸發器,分別對應Insert、Update、Delete等與數據更改相關的操作。例如Product數據表的觸發器:
FOR INSERT, UPDATE, DELETE AS BEGIN
SET NOCOUNT ON
EXEC dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure N'Product'
END
其中,AspNet_SqlCacheUpdateChangeIdStoredProcedure即是工具添加的一組存儲過程中的一個。當對Product數據表執行Insert、Update或Delete等操作時,就會激活觸發器,然后執行AspNet_SqlCacheUpdateChangeIdStoredProcedure存儲過程。其執行的過程就是修改AspNet_SqlCacheTablesForChangeNotification數據表的changeId字段值:
@tableName NVARCHAR(450)
AS
BEGIN
UPDATE dbo.AspNet_SqlCacheTablesForChangeNotification WITH (ROWLOCK) SET changeId = changeId + 1
WHERE tableName = @tableName
END
GO
4.2.2 利用SqlCacheDependencyAdmin類
我們也可以利用編程的方式來來管理數據庫對SqlCacheDependency特性的使用。該類包含了五個重要的方法:
DisableNotifications | 為特定數據庫禁用 SqlCacheDependency對象更改通知 |
DisableTableForNotifications | 為數據庫中的特定表禁用SqlCacheDependency對象更改通知 |
EnableNotifications | 為特定數據庫啟用SqlCacheDependency對象更改通知 |
EnableTableForNotifications | 為數據庫中的特定表啟用SqlCacheDependency對象更改通知 |
GetTablesEnabledForNotifications | 返回啟用了SqlCacheDependency對象更改通知的所有表的列表 |
表4-1 SqlCacheDependencyAdmin類的主要方法
假設我們定義了如下的數據庫連接字符串:
const string connectionStr = "Server=localhost;Database=MSPetShop4";
那么為數據庫MSPetShop4啟用SqlCacheDependency對象更改通知的實現為:
{
if (!IsPostBack)
{
SqlCacheDependencyAdmin.EnableNotifications(connectionStr);
}
}
為數據表Product啟用SqlCacheDependency對象更改通知的實現則為:
如果要調用表4-1中所示的相關方法,需要注意的是訪問SQL Server數據庫的帳戶必須具有創建表和存儲過程的權限。如果要調用EnableTableForNotifications方法,還需要具有在該表上創建SQL Server觸發器的權限。
雖然說編程方式賦予了程序員更大的靈活性,但aspnet_regsql工具卻提供了更簡單的方法實現對SqlCacheDependency的配置與管理。PetShop 4.0采用的正是aspnet_regsql工具的辦法,它編寫了一個文件名為InstallDatabases.cmd的批處理文件,其中包含了對aspnet_regsql工具的執行,并通過安裝程序去調用該文件,實現對SQL Server的配置。