当缓存数据时,基于时间周期的技术因为其易用性而常常被采用,不过又常常不那么完美。理想的状态是这样的:数据库数据还 是应缓存在内存,直到源数据(underlying data)发生改变时才从内存清除。这样的话可以最大化的获取缓存带来的性能上的好处,同时使“过时数据”(stale data)持续的时间最短。然而,为此,们需要建立一种机制来探测数据库数据什么时候发生了改变,并将对应的缓存条目清除掉。
ASP.NET 2.0提供的SqlCacheDependency class类和必要的下部基础构造(infrastructure)可以判断数据库什么时候发生了更改,以便将对应的缓存条目从内存清除掉。有2种技术可 以判断源数据在什么时候发生了改变:polling 和notification。
理解Notification and Polling
有2种方法来判断一个数据库里的数据在什么时候修改过:notification 和 polling.当使用notification的时候,数据库提示(alerts)ASP.NET对应某个具体查询的数据已经发生了改变;于是对应的缓 存条目将被清除。使用polling的时候,数据库服务器将包含某个表(tables)最近发生更改时的相关信息。ASP.NET周期性的对数据库进行检 查,看哪些表在数据被缓存以后发生过改动,若改动过,对应的缓存条目将被清除。
notification是对查询(query)而不是表 (table)进行跟踪检查,相对polling而言,需要采取的步骤要少些。不过遗憾的是,只有在Microsoft SQL Server 2005的完整版(也就是non-Express版本)才能使用该功能。而Microsoft SQL Server的所有版本,从7.0 到2005都可以使用polling功能.
除了表AspNet_SqlCacheTablesForChangeNotification外,数据库还需要为出现在SQL cache dependency里的每个表包含一个触发器(triggers),任何时候,只要表插入、更新、删除一条记录或在表 AspNet_SqlCacheTablesForChangeNotification里的对应的changeId值增大的情况下就会执行触发器。
当使用SqlCacheDependency对象(object)来缓存数据时,ASP.NET将关注某个表的当前(current)的changeId 值,一旦发现当前其值与数据库里面的changeId值不同时,就将该SqlCacheDependency对象清除。因为,changeId不吻合就意 味着在完成数据缓存后,表又发生过改动。
第一步:考察命令行程序aspnet_regsql.exe
使用polling方法时,必须对数据库进行设置以包含这些基础构造:一个预先定义的表 (AspNet_SqlCacheTablesForChangeNotification),一些存储过程,以及基于在SQL cache dependencies里要用到的表的触发器。诸如这些表、存储过程、触发器等都可以通过命令行程序aspnet_regsql.exe来创建,该命令 位于$WINDOWS$\Microsoft.NET\Framework\version文件夹。要创建表 AspNet_SqlCacheTablesForChangeNotification以及相关的存储过程,可以在命令行这样运行:
/* For SQL Server authentication... */
aspnet_regsql.exe -S server -U user -P password -d database -ed
/* For Windows Authentication... */
aspnet_regsql.exe -S server -E -d database -ed
注意:要运行这些命令,必须以db_securityadmin 和 db_ddladmin的身份登录数据库,
比如:在Windows身份认证模式下,对某个数据库服务器ScottsServer里的数据库pubs添加基础构造时,在命令行键入:
aspnet_regsql.exe -S ScottsServer -E -d pubs -ed
完成了数据库级(database-level)基础构造的添加后,我们需要添加触发器,再次使用aspnet_regsql.exe命令,不过用-t来指定"表名"(table name),且将-ed替换为-et,如下:
/* For SQL Server authentication... */
aspnet_regsql.exe -S <i>server</i>
-U <i>user</i> -P <i>password</i> -d <i>database</i> -t <i>tableName</i> -et
/* For Windows Authentication... */
aspnet_regsql.exe -S <i>server</i>
-E -d <i>database</i> -t <i>tableName</i> -et
要对ScottsServer里的表authors和titles添加触发器,这样做:
aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et
就本文而言,我们要对表Products, Categories,和Suppliers添加触发器,
第二步:在文件夹App_Data里引用一个Microsoft SQL Server 2005 Express版的数据库
为了添加必要的基础构造,aspnet_regsql.exe命令需要用到数据库和服务器的名称。但是对于放在文件夹App_Data里的一个 Microsoft SQL Server 2005 Express的数据库而言,它的数据库名和服务器名又是什么呢?犯不着探究其数据库名和服务器名到底是什么,我发现最简单的方法是用SQL服务管理器 (SQL Server Management Studio)来将该数据库认作localhost\SQLExpress database数据库,并重新命名。如果你的机器里已经安装了SQL Server 2005完整版,自然也就安装了SQL服务管理器。如果你安装的是Express版本的话,你可以免费下载Microsoft SQL Server Management Studio Express Edition.
(http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=C243A5AE-4BD1-4E3D-94B8-5A0F62BF7796)
首先,关闭Visual Studio,然后打开SQL Server Management Studio,在Windows Authentication模式里选择连接到localhost\SQLExpress.
接到服务器后,管理器将显示服务器,并将数据库、安全等以折叠的形式显示出来。在数据库文件夹上右击,选添加(Attach)项,这样将弹出Attach Databases对话框(见图2),点Add按钮,选择我们的web应用程序的App_Data文件夹里的NORTHWND.MDF数据库。
这样将会把数据库添加到Databases文件夹,且数据库的名称可能是该数据库文件的绝对路径(full path).出于简便的原则,我们将其重命名为一个更友好(human-friendly)的名字,我将其命名为“DataTutorials”.
第三步:对Northwind数据库添加Polling基础构造
现在我们添加了App_Data文件夹里的NORTHWND.MDF数据库,让我们添加polling 基础构造吧,假定你已经将数据库重命名为“DataTutorials”, 运行如下的命令:
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -ed
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Products -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Categories -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Suppliers -et
完成上述4个命令后,在Management Studio里右击数据库,进入任务子菜单(Tasks submenu),选分派(Detach)。然后关闭Management Studio并重新打开Visual Studio.
打开Visual Studio后,在服务器资源管理器里展开数据库,你可以看到有新增的表 (AspNet_SqlCacheTablesForChangeNotification),新的存储过程,以及对应于表Products, Categories, 和Suppliers的触发器.
第四步:设置Polling服务
完成上述步骤后,最后我们需要设置polling服务。这要用到Web.config文件,在里面指定要用到的数据库,以及检测频率(polling frequency),单位为毫秒。下面的代码是每隔1秒对Northwind数据库检测一次。
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="NORTHWNDConnectionString" connectionString= "Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
...
<!-- Configure the polling service used for SQL cache dependencies -->
<caching>
<sqlCacheDependency enabled="true" pollTime="1000" >
<databases>
<add name="NorthwindDB" connectionStringName="NORTHWNDConnectionString" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
在<add>元素里的name值(“NorthwindDB”)是一个易读(human- readable)的名称,它与某个具体的数据库对应。当使用SQL cache dependencies的时候,我们需要引用在这里定义的数据库名。我们将在第六步考察怎样使用SqlCacheDependency class类来缓存数据。
一旦确定了一个SQL cache dependency后,检测系统(polling system)每隔定义的pollTime那么多毫秒对<databases>元素里的数据库进行连接,并执行名为 AspNet_SqlCachePollingStoredProcedure的存储过——该存储过程是我们在第三步使用 aspnet_regsql.exe命令行工具添加的,它返回的是表 AspNet_SqlCacheTablesForChangeNotification里的每条记录的tableName 和 changeId值。那些“过时”的SQL cache dependencies将会从内存清除掉。
应在权衡性能和数据刷新(data staleness)的基础上设置pollTime.较小的pollTime值虽然导致请求数据库的次数增加,但能更快的将“过时"的数据清除掉;较大的 pollTime值虽然减少了对数据库的请求次数,但增加了“过时”的缓存条目的呆滞时间。还好,对数据库的请求只是执行一个简单的存储过程而已,该存储 过程仅仅从一个简单的表返回很少的几行。你最好多测试几个不同的pollTime值,在平衡数据库访问和数据刷新2方面的情况下找出一个理想的值。 pollTime值最小允许为500.
注意:在上面的代码里,我们在<sqlCacheDependency>元素里指定了一个单一的pollTime值。其实你也可以在< add>元素里随意的指定一个pollTime值。当你指定了很多个数据库,且你想为每个数据库都指定一个检测频率(polling frequency)时,这样做很有用。
第五:声明SQL Cache Dependencies
在第一到第四步骤,我们探讨了如何建立必需的数据库基础构造,以及设置检测系统(polling system).完成上述步骤后,现在我们可以通过编程或声明的方式,在添加缓存条目时使用SQL cache dependency.在本节,我们探讨如何使用声明的方式使用SQL cache dependencies,
ObjectDataSource控件可以使用单个或多个SQL cache dependencies.
只有当ObjectDataSource控件从它的相关“源对象”(underlying object)获取数据时才会触发它的Selecting事件。如果ObjectDataSource是从内存检索数据的话,将不会触发Selecting事件.
除了EnableCaching属性以外,ObjectDataSource控件还有SqlCacheDependency property属性,它可以为缓存数据添加一个或更多的SQL cache dependencies.像下面这样:
databaseName1:tableName1;databaseName2:tableName2;...
其中,databaseName是Web.config文件里<add>元素的name属性指定的数据库 名,而tableName就是数据库里的一个表。举个例,要创建一个ObjectDataSource,它用SQL cache dependency来缓存数据,当我们指定要用Northwind数据库里的 Products表时,我们将ObjectDataSource的EnableCaching属性设置为true,且 SqlCacheDependency属性为“NorthwindDB:Products”.
注意:你可以通过设置EnableCaching属性为true来使用一个 SQL cache dependency和基于时间的缓存期(time-based expiry)。CacheDuration对应时间间隔;SqlCacheDependency对应数据库名和表名。不管是缓存到期还是检查系统 (polling system)发现“源数据”发生改变,只要其中一个发生, ObjectDataSource 都会将清除其数据。
在SqlCacheDependencies.aspx页面里的GridView 控件从2个表获取数据——Products 和 Categories (产品的CategoryName列是通过语法 JOIN on Categories来获取的). 因此,我们想指定2个SQL cache dependencies:
“NorthwindDB:Products;NorthwindDB:Categories”.
设置SQL Cache Dependencies支持缓存后,再次来浏览器里登录页面。最开始,文本“—Selecting event fired”依然会出现在页面里,但当进行分页、排序或点击编辑和取消按钮时,文本就消失了。这是因为对数据进行缓存后,其缓存状态一直持续,直到 Products 或 Categories表发生了改变,或我们通过GridView对数据进行了更新。
第六步:通过编程的方式处理SqlCacheDependency类
在检测系统(polling system)里,一个SqlCacheDependency对象必须与某个具体的数据库和表挂钩。下面的代码,创建了一个SqlCacheDependency对象,它基于Northwind数据库的Products表:
Caching.SqlCacheDependency productsTableDependency = new Caching.SqlCacheDependency("NorthwindDB", "Products");
上面的2个参数分别对应数据库名和表名。与ObjectDataSource控件的属性SqlCacheDependency类似,数据库名是使用的Web.config.文件里<add> 元素的name属性指定的值,而表名是实际的数据库表名.。
要将一个SqlCacheDependency与添加到内存的条目联系起来,可以使用一个重载的接受dependency的Insert方法。下面代码里 的SqlCacheDependency基于表Products,且缓存时间未定。换句话说,数据会一直保存在内存,除非内存不足或表Products发 生了改变才被清除掉。
Caching.SqlCacheDependency productsTableDependency = new Caching.SqlCacheDependency("NorthwindDB", "Products");
Cache.Insert(key,
value,
productsTableDependency,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration);
目前,缓存层Caching Layer的ProductsCL类从表Products获取数据,缓存时间为60秒。 让我们对其进行更新,使其使用SQL cache dependencies. 类ProductsCL的AddCacheItem方法是用来向内存添加数据的,其当前代码如下:
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Make sure MasterCacheKeyArray[0] is in the cache
DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
// Add a CacheDependency
Caching.CacheDependency dependency =
new Caching.CacheDependency(null, MasterCacheKeyArray);
DataCache.Insert(GetCacheKey(rawKey), value, dependency,
DateTime.Now.AddSeconds(CacheDuration),
System.Web.Caching.Cache.NoSlidingExpiration);
}
让我们对其进行更新,用一个SqlCacheDependency对象来替换掉MasterCacheKeyArray cache dependency:
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Add the SqlCacheDependency objects for Products
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
// Add the item to the data cache using productsTableDependency
DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency,
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}
第七步:对缓存条目附加多个Dependencies
我们知道, MasterCacheKeyArray cache dependency的用处在于:与 product相关的所有条目中,只要其中任意一条的相关数据发生更改后,所有的条目都会被清除掉。举个例, GetProductsByCategoryID(categoryID)方法根据指定的categoryID获取多条产品记录并缓存,只要其中任何一条 记录被清除掉的话, MasterCacheKeyArray cache dependency 会确保剩下的其它记录也会被清除掉。
没有MasterCacheKeyArray cache dependency的话,就会存在这种可能性,当某个条目更改过后,剩余的条目仍然驻留在内存而显得“过时”。因此,在使用SQL cache dependencies的时候包含MasterCacheKeyArray cache dependency是很重要的。然而,data cache的Insert 方法只允许存在一个dependency 对象。
此外,当使用SQL cache dependencies的时候,我们可能要依赖多个表。比如,ProductsCL类的ProductsDataTable里还包含了每个产品的种类 (category)和供应商名称(supplier names).但是在AddCacheItem方法里,只依赖 表Products.设想,如果用户更新了种类或供应商,那么缓存的product数据仍然驻留在内存,显然已经"过时"了。因此,我们想时缓存的 product数据不仅依赖Products表,还要依赖Categories 和 Suppliers 表.
不过类AggregateCacheDependency提供了这个途径,将一个缓存条目与多个 dependencies联系起来。首先,创建一个AggregateCacheDependency实例;然后用 AggregateCacheDependency的 Add 方法添加设置好的dependencies.当AggregateCacheDependency 实例里的任何一个dependencies发生改动以后,缓存条目就会被清除掉。
下面的代码是更新过的ProductsCL类的 AddCacheItem 方法。该方法不仅创建了MasterCacheKeyArray cache dependency,还创建了基于表Products, Categories,和 Suppliers的多个SqlCacheDependency objects对象。再把它们组合起来构成一个名为aggregateDependencies的AggregateCacheDependency object 对象,将该对象传递给Insert方法.
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Make sure MasterCacheKeyArray[0] is in the cache and create a depedency
DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
Caching.CacheDependency masterCacheKeyDependency =
new Caching.CacheDependency(null, MasterCacheKeyArray);
// Add the SqlCacheDependency objects for Products, Categories, and Suppliers
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
Caching.SqlCacheDependency categoriesTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Categories");
Caching.SqlCacheDependency suppliersTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Suppliers");
// Create an AggregateCacheDependency
Caching.AggregateCacheDependency aggregateDependencies =
new Caching.AggregateCacheDependency();
aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency,
categoriesTableDependency, suppliersTableDependency);
DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies,
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}
对代码进行测试。现在,更改表Products、 Categories, 或Suppliers的话将清除掉缓存数据。另外,当在GridView控件里编辑某个产品的话将调用ProductsCL 类的 UpdateProduct方法,该方法清除掉 MasterCacheKeyArray cache dependency,进而导致连锁反应清除掉缓存的ProductsDataTable.最后的结果是,当下次请求数据时将重新从数据库检索数据。
注意:也可以通过output caching来使用SQL cache dependencies.
使用SQL Cache Dependencies (PS:此例主要用于7.0--2005express)
来源:博客
作者:icyleaf1026
时间:2008-04-08 点击:290 次
相关文章:
文章搜索
推荐文章
推荐产品
|
VARCHART XGantt
全球知名的甘特图控件,能够实现如 Microsoft Project 般强大的项目管理功能。
|
|
TurboDemo 中文版
TurboDemo - 抓取屏幕截图并通过动态演示示例及手册解释软件、个人电脑应用程序、网站与产品。
|
|
BCGControlBar Library .NET Edition
该组件包含大量可自定义程度高、可设计性好的组件,使用户可创建精致美观的图形用户界面。
|
|
WebUI Studio.NET
WebUI Studio.NET 是一套用于开发专业 ASP.NET web 应用程序必不可少的控件。
|





