問題背景: 最近做一個非常簡單的功能,就是使用ajax請求的方式從服務端請求一段下拉表的數據。 以前也有做過這個功能,只不過這次做這個功能的時候冒出了一個想法: 我請求的這段數據它是一段相對比較固定的數據,也就是說它不怎麼改變,也許幾個月才會改變一次。由於這種數據的變化周期很長,所以以前做這種功能的 ...
問題背景:
最近做一個非常簡單的功能,就是使用ajax請求的方式從服務端請求一段下拉表的數據。
以前也有做過這個功能,只不過這次做這個功能的時候冒出了一個想法:
我請求的這段數據它是一段相對比較固定的數據,也就是說它不怎麼改變,也許幾個月才會改變一次。由於這種數據的變化周期很長,所以以前做這種功能的時候,會使用緩存進行優化,可以直接從緩存中讀取數據,避免每一次接收了ajax請求後都要向資料庫要數據,減少伺服器與資料庫之間的交互,減輕資料庫伺服器的壓力。但是問題來了,數據的變化周期再長終究是要變化的,當資料庫中的數據變化的時候你就要對舊有的緩存內容進行移除(remove)操作,好的,以前我是這樣做的:
public ActionResult GetAllFJs() { //嘗試從緩存中取出數據 var FJIdNames = HttpContext.Cache["FJIdNames"]; if (null == FJIdNames) //緩存中沒有數據 { //從資料庫中查詢出數據 FJIdNames = FJService.GetAll().Select(fj => new { Id = fj.Id, Name = fj.Name }).ToArray(); //將數據緩存起來下一次用(設置一個緩存有效時間,使用絕對過期,到達指定過期時間,緩存失效) HttpContext.Cache.Insert( "FJIdNames", //緩存項的鍵(string) FJIdNames, //緩存項的值(object) null, DateTime.UtcNow.AddMinutes(30), //絕對到期時間(DateTime) System.Web.Caching.Cache.NoSlidingExpiration //滑動到期間隔時間(TimeSpan) ); } //將得到的數據轉化成json格式字元串 string jsonResult = CommonHelper.ConvertToJsonStr(FJIdNames); //返回給瀏覽器的結果字元串 return Content(jsonResult); }
說明:這是一個 asp.net MVC 中處理一個請求的action方法,其中有一個重要的方法
public void Insert(
string key, --> 緩存項的鍵
object value, --> 緩存項的值
CacheDependency dependencies, --> 緩存依賴項(這裡不用,後面會用,是重頭戲)
DateTime absoluteExpiration, --> 絕對過期時間點
TimeSpan slidingExpiration --> 滑動過期時間間隔
);
在這裡是利用了緩存的過期時間來對緩存數據進行更新操作,每當緩存數據經過了30分鐘後就要重新從資料庫中拿一次數據來更新緩存中的內容,來看一下執行的結果 (為了便於展示結果,將絕對過期時間調為30s):
第一次請求:
(資料庫表中此刻的數據):
(執行結果):
第一次請求的話肯定緩存中是沒有數據的,所以要向資料庫要數據;
第二次請求:
(請求之前改變一下資料庫中相應表的數據)
(執行結果):
第二次請求,由於沒有到緩存項的絕對過期時間,緩存中還有數據,不用向資料庫要數據,從緩存中取出來就行;
過30s後再請求:
(資料庫表中此刻的數據):
(執行結果):
緩存中數據已經過期被移除,需要重新向資料庫請求;
從以上三次請求可以看到,最後的確是實現了緩存數據的更新。
但同樣可以看到這樣做有一個壞處:在還沒到達過期時間的這段時間里,請求的數據依然是原來的緩存中數據,和資料庫中的數據並不一致。
其中設置的絕對過期時間點要根據實際的數據刷新的可容忍度來進行設定,而恰好在我的這個應用場景中的可容忍度最不能把握,它要求的是 當資料庫中的數據改變以後,緩存中對應的數據在下一次請求結束後一定要馬上跟著改變,當然你也可以把過期時間儘可能的調小,調到一秒。當然,這樣的話還是要頻繁的向資料庫進行請求,那不是背離了我們原本使用緩存優化的目的了嗎?
所以現在的問題是:有沒有一種方法能讓資料庫和伺服器程式建立一種聯繫,這種聯繫好比是一種“心靈感應”,當資料庫表中的數據發生變化的時候,馬上就能讓伺服器中的對應的緩存項“感應”到這個變化,從而讓原來的緩存項失效呢?答案當然是,有的。
資料庫依賴緩存,對,是它,就是它。
ASP.NET 有 3 種類型的依賴:
- 緩存項目依賴
- 文件或文件夾依賴
- 資料庫依賴
本文要講的是資料庫依賴:資料庫緩存依賴是一項當資料庫中的相關數據被修改時自動使緩存的數據對象失效的技術。
以前不知道有 資料庫依賴 的時候,有想過使用文件或文件夾依賴來實現類似於資料庫依賴緩存的功能,大概的思路就是:用某個文件作為媒介,在進行了對資料庫表的數據改動後,同時改動一下該文件來觸發緩存的失效。還好我在看了大神們寫的博文以後,馬上停止了我“愚蠢”的想法,那樣做其實是畫蛇添足,而且並不能很好的實現,有現成的資料庫依賴不用幹嘛。
我們來看一下如何使用它:
1、配置:
1)在當前網站mvc項目下的Web.config文件中加入
<!--(資料庫連接字元串)<configuration> 結點下配置-->
<connectionStrings> <add name="connStr" connectionString="server=127.0.0.1; user id=sa; password=root; database=LZXF" providerName="System.Data.SqlClient" /> </connectionStrings>
<!--(緩存資料庫依賴配置)<system.web> 結點下配置--> <caching> <sqlCacheDependency enabled="true"> <databases> <add name="LZXF" pollTime="5000" connectionStringName="connStr" /> </databases> </sqlCacheDependency> </caching>
2)在 Application_Start() 中加入
//配緩存資料庫依賴 string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["connStr"].ToString(); //啟動資料庫的數據緩存依賴功能 SqlCacheDependencyAdmin.EnableNotifications(connectionString); //啟用數據表緩存 SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString, "(表名)"); //第二個參數可以是單個表名或表名的數組
2、代碼部分
public ActionResult GetAllFJs() { //嘗試從緩存中取出數據 var FJIdNames = HttpContext.Cache["FJIdNames"]; if (null == FJIdNames) //緩存中沒有數據 { //從資料庫中查詢出數據 FJIdNames = FJService.GetAll().Select(fj => new { Id = fj.Id, Name = fj.Name }).ToArray(); //將數據緩存起來下一次用(使用資料庫依賴的緩存,當資料庫中對應的表的數據發生改變時,緩存失效) HttpContext.Cache.Insert("FJIdNames", FJIdNames, new SqlCacheDependency("LZXF", "T_FJs")); } //將得到的數據轉化成json格式字元串 string jsonResult = CommonHelper.ConvertToJsonStr(FJIdNames); //返回給瀏覽器的結果字元串 return Content(jsonResult); }
其中的 SqlCacheDependency(資料庫緩存依賴類) 是最重要的一個類,就是它建立起了資料庫和伺服器程式之間 “溝通的橋梁” ,
使用它的一個構造方法:
public SqlCacheDependency(string databaseEntryName, string tableName) 來創建一個資料庫緩存依賴類對象,傳給創建緩存項的方法Insert, 這樣來建立該緩存項的資料庫依賴,每當該指定表中發生數據的變動時都會銷毀該緩存項。
看一下執行結果:
沒改變資料庫表數據之前:
(執行結果):
改變資料庫表數據之後:
(執行結果):
改變了資料庫表的數據以後再去請求數據,請求到最新的數據,說明舊的緩存被移除了
既然都會用了,那接下來要看的就是原理,(原理,原理最重要)
用完了以後就會很疑惑,它是怎麼實現的呢?
思考:首先是改動了資料庫表中的數據,這一步操作以後必定要引發某種操作。在資料庫中改變表中數據會觸發操作?講的不就是觸發器嗎。
來看一下資料庫中多了些什麼:
打開sqlServerManagement查看資料庫發現
多了一個 AspNet_SqlCacheTablesForChangeNotification 表,表中內容:
要找的觸發器在這裡:
看一下觸發器裡面的內容:
說的就是:當 T_FJs 表中發生了 INSERT, UPDATE, DELETE 這些操作,就去執行 dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure 這個存儲過程,這個存儲過程是我們開啟sql server的緩存依賴自動生成的
找一下存儲過程 dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure,在這裡:
內容是:
所以最後對原理的總結,我就引用一篇大神博客(https://www.lanhusoft.com/Article/290.html)里的總結:
當sql server啟用緩存依賴之後,就會在對應的資料庫添加相應的表、觸發器和一些存儲過程。它是利用觸發器來監測表的數據的變化,如果有增、刪、改就插入數據到通知表,然後通知訂閱這個通知的網站此緩存項失效。
最後要說的是使用緩存依賴也有限制:必須用ASP.Net和SQL Server開發應用,也就是SqlCacheDependency是基於微軟的那套體制。
參考博文地址:
https://www.cnblogs.com/SkySoot/archive/2012/08/15/2640559.html
https://www.lanhusoft.com/Article/290.html
這是我的第一篇博文,以前想過為什麼要寫博文這個問題?現在網上隨便一搜都有自己想要的內容,而且有些大神寫的很不錯。後來慢慢的發現每一次遇到問題都要上網搜博客看,而且很多時候遇到的是同一個問題,可能以前就已經思考過了,現在又要浪費時間去從頭找著看已經看過的東西,那我為什麼不自己寫好了整理好了存著呢。當然,方便自己的同時,希望也能方便到正在閱讀本文的你。謝謝!