一、 Cache概述
既然緩存中的數據其實是來自數據庫的,那麼緩存中的數據如何和數據庫進行同步呢?一般來說,緩存中應該存放改動不大或者對數據的實時性沒有太多要求的數據。這樣,我們只需要定期更新緩存就可以了。相反,如果緩存的更新頻率過快的話,使用緩存的意義就不是很大了,因此更新緩存的時候需要一次性從數據庫中讀取大量的數據,過於頻繁地更新緩存反而加重了數據庫的負擔。
那麼ASP.NET中的Cache又提供了哪些緩存的過期策略呢?
· 永不過期。和Application一樣,緩存永不過期。
· 絕對時間過期。緩存在某一時間過期,比如5分鐘後。
· 變化時間過期(平滑過期)。緩存在某一時間內未訪問則超時過期,這個和Session有點類似,比如我們可以設定緩存5分鐘沒有人訪問則過期。
· 依賴過期。緩存依賴於數據庫中的數據或者文件中的內容。一旦數據庫中某些表的數據發生變動或者文件內容發生變動,則緩存自動過期。
緩存過期後我們就要更新緩存了,ASP.NET提供了兩種更新策略。
· 被動更新。緩存過期以後手動進行更新。
· 主動更新。緩存過期以後在回調方法中更新。
二、 Cache性能與過期策略
首先,在頁面上添加兩個按鈕,並雙擊按鈕實現Click事件處理方法。
程序代碼
<asp:Button ID=\"btn_GetDataFromCache\" runat=\"server\" OnClick=\"btn_GetData_Click\"
Text=\"從緩存中讀取數據\" />
<asp:Button ID=\"btn_GetDataFromDb\" runat=\"server\" OnClick=\"btn_GetDataFromDb_Click\"
Text=\"從數據庫中讀取數據\" />
第一個按鈕實現從緩存讀取數據。
注意:本例需要using以下命名空間。
程序代碼
using System.Diagnostics; // 用於精確測定時間間隔
using System.Web.Caching; // 用於緩存的策略
using System.IO; // 用於文件操作
protected void btn_GetData_Click(object sender, EventArgs e)
{
InsertRecord();
Stopwatch sw=new Stopwatch();
sw.Start();
if (Cache[\"Data\"]==null)
{
Response.Write(\"緩存無效<br/>\");
}
else
{
DataSet ds = Cache[\"Data\"] as DataSet;
Response.Write(string.Format(\"查詢結果:{0}<br/>\", ds.Tables[0].Rows[0][0]));
Response.Write(string.Format(\"耗費時間:{0}<br/>\", sw.ElapsedTicks));
}
}
在這裡有幾點需要說明。
· 一開始的InsertRecord()方法是我們自己創建的,用來向數據庫插入一條記錄。這樣,我們就能看出來數據是否是從緩存中讀取的了。
InsertRecord()方法如下:
程序代碼
private void InsertRecord()
{
using (SqlConnection conn = new SqlConnection(@\"server=(local)\\SQLEXPRESS;
database=Forum;Trusted_Connection=True\"))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(\"Insert into CacheTest (Test) values
(\'Test\')\", conn))
{
cmd.ExecuteNonQuery();
}
}
}
· 如果緩存存在則輸出查詢結果和查詢耗費的時間,如果緩存不存在則輸出“緩存無效”。
· Stopwatch類用於精確測定逝去的時間,ElapsedTicks屬性返回了間隔的計數器刻度,所謂計數器刻度就是系統的計數器走過了多少次。當然,Stopwatch還有ElapsedMilliseconds能返回間隔的總毫秒數。之所以使用ElapsedTicks,因為它是一個更小的時間單位。
第二個按鈕直接從數據庫讀取數據。
程序代碼
protected void btn_GetDataFromDb_Click(object sender, EventArgs e)
{
InsertRecord();
Stopwatch sw = new Stopwatch();
sw.Start();
DataSet ds = GetData();
Response.Write(string.Format(\"查詢結果:{0}<br/>\", ds.Tables[0].Rows[0][0]));
Response.Write(string.Format(\"耗費時間:{0}<br/>\", sw.ElapsedTicks));
}
在這裡,我們把讀取數據的操作使用一個GetData()方法進行了封裝,方法實現如下:
程序代碼
private DataSet GetData()
{
DataSet ds = new DataSet();
using (SqlConnection conn = new SqlConnection(@\"server=(local)\\SQLEXPRESS;
database=Forum;Trusted_Connection=True\"))
{
SqlDataAdapter da = new SqlDataAdapter(\"select count(*) from CacheTest\", conn);
da.Fill(ds);
}
return ds;
}
為了能體現出緩存的效率,我們在Forum數據庫中又新建立了一個CacheTest數據表,表結構很簡單,如圖4-1所示。
圖4-1 CacheTest表結構
我們在表中插入了10萬條以上的記錄,使得表的大小達到了100MB左右。
運行程序,單擊“從數據庫中讀取數據”按鈕,如圖4-2所示,
圖4-2 從數據庫讀取數據需要花費大量的時間
我們可以看到,這個操作耗費了相當多的時間。
因為我們直接從數據庫讀取count(*),所以每次單擊按鈕查詢結果顯示的數字都會+1。現在你單擊“從緩存中讀取數據”肯定是顯示“緩存無效”,因為我們還沒有添加任何緩存。
然後,我們在頁面上添加三個按鈕並雙擊按鈕創建事件處理方法,三個按鈕使用不同的過期策略添加緩存。
程序代碼
<asp:Button ID=\"btn_InsertNoExpirationCache\" runat=\"server\" Text=\"插入永不過期緩存\"
OnClick=\"btn_InsertNoExpirationCache_Click\" />
<asp:Button ID=\"btn_InsertAbsoluteExpirationCache\" runat=\"server\" Text=\"插入絕對時間
過期緩存\" OnClick=\"btn_InsertAbsoluteExpirationCache_Click\" />
<asp:Button ID=\"btn_InsertSlidingExpirationCache\" runat=\"server\" Text=\"插入變化時間
過期緩存\" OnClick=\"btn_InsertSlidingExpirationCache_Click\" />
三個按鈕的Click事件處理方法如下:
程序代碼
protected void btn_InsertNoExpirationCache_Click(object sender, EventArgs e)
{
DataSet ds = GetData();
Cache.Insert(\"Data\", ds);
}
protected void btn_InsertAbsoluteExpirationCache_Click(object sender, EventArgs e)
{
DataSet ds = GetData();
Cache.Insert(\"Data\", ds,null, DateTime.Now.AddSeconds(10), TimeSpan.Zero);
}
protected void btn_InsertSlidingExpirationCache_Click(object sender, EventArgs e)
{
DataSet ds = GetData();
Cache.Insert(\"Data\", ds, null, DateTime.MaxValue, TimeSpan.FromSeconds(10));
}
我們來分析一下這三種過期策略。
· 永不過期。直接賦值緩存的Key和Value即可
· 絕對時間過期。DateTime.Now.AddSeconds(10)表示緩存在10秒後過期,TimeSpan.Zero表示不使用平滑過期策略。
· 變化時間過期(平滑過期)。DateTime.MaxValue表示不使用絕對時間過期策略,TimeSpan.FromSeconds(10)表示緩存連續10秒沒有訪問就過期。
在這裡,我們都使用了Insert()方法來添加緩存。其實,Cache還有一個Add()方法也能向緩存中添加項。不同之處在於Add()方法只能添加緩存中沒有的項,如果添加緩存中已有的項將失敗(但不會拋出異常),而Insert()方法能覆蓋原來的項。
注意:和Application不同,這裡不需要使用在插入緩存的時候進行鎖操作,Cache會自己處理 並發。
現在,我們就可以打開頁面對這三種過期策略進行測試了。
1.單擊“從緩存中讀取數據”按鈕,提示“緩存無效”。
2.單擊“從數據庫中讀取數據”按鈕,查詢結果顯示現在記錄總數為100646。
3.單擊“插入永不過期緩存”按鈕,然後連續單擊“從緩存中讀取數據”按鈕,可以發現,無論過去多久,緩存始終沒有過期,而且觀察記錄查詢結果可以發現值始終沒有發生變化。不同的是,從緩存中讀取數據的效率比從數據庫中讀取數據提高了幾個數量級,如圖4-3所示,你可以和圖4-2進行比較。
圖4-3 從緩存中讀取數據所花費的時間
4.單擊“插入絕對時間過期緩存”,然後連續單擊“從緩存中讀取數據”按鈕,大約10秒過期後,頁面提示“緩存無效”,說明緩存過期了。
5.單擊“插入變化時間過期緩存”,然後連續單擊“從緩存中讀取數據”按鈕,緩存始終不過期,如果我們等待10秒後再去單擊按鈕,頁面提示“緩存無效”,說明緩存過期了。
我們再來看一下依賴過期策略。所謂依賴過期就是緩存的依賴項(比如一個文件)的內容改變之後緩存也就失效了。由於篇幅關系,這裡只介紹文件依賴。我們在頁面上再加兩個按鈕並雙擊按鈕添加Click事件處理方法。
程序代碼
<asp:Button ID=\"btn_ModifyFile\" runat=\"server\" Text=\"修改文件\" OnClick=\"btn_ModifyFile_
Click\" />
<asp:Button ID=\"btn_AddFileDependencyCache\" runat=\"server\" Text=\"插入文件依賴緩存\"
OnClick=\"btn_AddFileDependencyCache_Click\" />