Caching
了解缓存对于性能良好的 ASP.NET 应用程序非常重要。 ASP.NET 1.x 提供了三种不同的缓存选项:输出缓存、片段缓存和缓存 API。
了解缓存对于性能良好的 ASP.NET 应用程序非常重要。 ASP.NET 1.x 提供了三种不同的缓存选项:输出缓存、片段缓存和缓存 API。 ASP.NET 2.0 提供了这三种方法,但它增加了一些重要的附加功能。 有几个新的缓存依赖项,开发人员现在也可以选择创建自定义缓存依赖项。 在 ASP.NET 2.0 中,缓存的配置也得到了显著改进。
新功能
缓存配置文件
缓存配置文件允许开发人员定义特定的缓存设置,这些设置随后可以应用于各个页面。 例如,如果某些页面应在 12 小时后从缓存过期,则可以轻松创建可应用于这些页面的缓存配置文件。 若要添加新的缓存配置文件,请使用 <配置文件中的 outputCacheSettings> 部分。 例如,下面是名为 twoday 的缓存配置文件的配置,该配置文件将缓存持续时间配置为 12 小时。
<outputCacheSettings>
<outputCacheProfiles>
<add name="TwoDay" duration="43200" />
</outputCacheProfiles>
</outputCacheSettings>
若要将此缓存配置文件应用于特定页面,请使用 @ OutputCache 指令的 CacheProfile 属性,如下所示:
<%@ OutputCache CacheProfile="TwoDay" %>
自定义缓存依赖项
ASP.NET 1.x 开发人员对自定义缓存依赖项大声疾呼。 在 ASP.NET 1.x 中,CacheDependency 类已密封,这阻止开发人员从中派生自己的类。 在 ASP.NET 2.0 中,删除了该限制,开发人员可以自由开发自己的自定义缓存依赖项。 CacheDependency 类允许基于文件、目录或缓存密钥创建自定义缓存依赖项。
例如,下面的代码基于 Web 应用程序根目录中名为 stuff.xml 的文件创建新的自定义缓存依赖项:
System.Web.Caching.CacheDependency dep = new
System.Web.Caching.CacheDependency(Server.MapPath("stuff.xml"));
Response.AddCacheDependency(dep);
Cache.Insert("key", "value");
在这种情况下,当stuff.xml文件更改时,缓存的项将失效。
还可以使用缓存密钥创建自定义缓存依赖项。 使用此方法,删除缓存密钥将使缓存的数据失效。 以下示例对此进行了说明:
// insert a value into cache that will serve
// as the cache key
Cache["CacheKey"] = "something";
// create an array of cache keys
string[] keys = new String[1];
keys[0] = "CacheKey";
CacheDependency dep = new CacheDependency(null, keys);
// insert an item into cache with a dependency on
// the above CacheDependency
Cache.Insert("Key", "Value", dep);
若要使上面插入的项失效,只需删除插入缓存中的项即可充当缓存键。
// Remove the cache item that serves as the cache key
Cache.Remove("CacheKey");
请注意,充当缓存键的项的键必须与添加到缓存键数组的值相同。
Polling-Based SQL 缓存依赖项 (也称为Table-Based依赖项)
SQL Server 7 和 2000 对 SQL 缓存依赖项使用基于轮询的模型。 基于轮询的模型对数据库表使用触发器,该触发器在表中的数据更改时触发。 该触发器更新通知表中的 changeId 字段,ASP.NET 定期检查。 如果 changeId 字段已更新,ASP.NET 知道数据已更改,并且会使缓存的数据失效。
注意
SQL Server 2005 年也可以使用基于轮询的模型,但由于基于轮询的模型不是最有效的模型,因此建议使用基于查询的模型 (稍后) 2005 SQL Server讨论。
为了使使用基于轮询的模型的 SQL 缓存依赖项正常工作,表必须启用通知。 这可以通过编程方式使用 SqlCacheDependencyAdmin 类或使用 aspnet_regsql.exe 实用工具来实现。
以下命令行在 Northwind 数据库中注册 Products 表,该实例位于 SQL 缓存依赖项名为 dbase 的 SQL Server 实例上。
aspnet_regsql -S dbase -ed -d Northwind -E -et -t Products
下面是上述命令中使用的命令行开关的说明:
命令行开关 | 用途 |
---|---|
-S server | 指定服务器名称。 |
-ed | 指定应为 SQL 缓存依赖项启用数据库。 |
-d database_name | 指定应为 SQL 缓存依赖项启用的数据库名称。 |
-E | 指定aspnet_regsql在连接到数据库时应使用 Windows 身份验证。 |
-Et | 指定为 SQL 缓存依赖项启用数据库表。 |
-t table_name | 指定要为 SQL 缓存依赖项启用的数据库表的名称。 |
注意
还有其他开关可用于aspnet_regsql.exe。 有关完整列表,请运行 aspnet_regsql.exe -? 从命令行。
运行此命令时,将对 SQL Server 数据库进行以下更改:
- 添加 AspNet_SqlCacheTablesForChangeNotification 表。 此表包含数据库中已启用 SQL 缓存依赖项的每个表的一行。
- 在数据库中创建以下存储过程:
AspNet_SqlCachePollingStoredProcedure | 查询AspNet_SqlCacheTablesForChangeNotification表,并返回为 SQL 缓存依赖项启用的所有表以及每个表的 changeId 值。 此存储的 proc 用于轮询,以确定数据是否已更改。 |
---|---|
AspNet_SqlCacheQueryRegisteredTablesStoredProcedure | 通过查询AspNet_SqlCacheTablesForChangeNotification表返回为 SQL 缓存依赖项启用的所有表,并返回为 SQL 缓存依赖项启用的所有表。 |
AspNet_SqlCacheRegisterTableStoredProcedure | 通过在通知表中添加必要的条目并添加触发器,为 SQL 缓存依赖项注册表。 |
AspNet_SqlCacheUnRegisterTableStoredProcedure | 通过删除通知表中的 项来注销 SQL 缓存依赖项的表,并删除触发器。 |
AspNet_SqlCacheUpdateChangeIdStoredProcedure | 通过递增更改的表的 changeId 来汇报通知表。 ASP.NET 使用此值来确定数据是否已更改。 如下所述,此存储的 proc 由启用表时创建的触发器执行。 |
- 为表创建名为 table_name_AspNet_SqlCacheNotification_Trigger 的SQL Server触发器。 当对表执行 INSERT、UPDATE 或 DELETE 时,此触发器将执行AspNet_SqlCacheUpdateChangeIdStoredProcedure。
- 一个名为 aspnet_ChangeNotification_ReceiveNotificationsOnlyAccess 的SQL Server角色将添加到数据库中。
aspnet_ChangeNotification_ReceiveNotificationsOnlyAccess SQL Server角色对AspNet_SqlCachePollingStoredProcedure具有 EXEC 权限。 为了使轮询模型正常工作,必须将进程帐户添加到aspnet_ChangeNotification_ReceiveNotificationsOnlyAccess角色。 aspnet_regsql.exe工具不会执行此操作。
配置Polling-Based SQL 缓存依赖项
配置基于轮询的 SQL 缓存依赖项需要执行几个步骤。 第一步是启用数据库和表,如上文所述。 完成该步骤后,其余的配置如下所示:
- 配置 ASP.NET 配置文件。
- 配置 SqlCacheDependency
配置 ASP.NET 配置文件
除了添加上一模块中所述的连接字符串外,还必须使用 <sqlCacheDependency> 元素配置<缓存>元素,如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Pubs"
connectionString="Data Source=(local);
Initial Catalog=pubs;Integrated Security=true;"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled = "true" pollTime = "60000" >
<databases>
<add name="pubs" connectionStringName = "pubs" pollTime = "9000000" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
此配置支持对 pubs 数据库的 SQL 缓存依赖项。 请注意,sqlCacheDependency> 元素中的 <pollTime 属性默认为 60000 毫秒或 1 分钟。 (此值不能小于 500 毫秒。) 在此示例中, <add> 元素将添加新数据库并重写 pollTime,将其设置为 9000000 毫秒。
配置 SqlCacheDependency
下一步是配置 SqlCacheDependency。 最简单的实现方法是在 @ Outcache 指令中指定 SqlDependency 属性的值,如下所示:
<%@ OutputCache duration="60"
VaryByParam="none" SqlDependency="pubs:authors" %>
在上面的 @ OutputCache 指令中,为 pubs 数据库中的作者表配置了 SQL 缓存依赖项。 可以通过使用分号分隔多个依赖项来配置这些依赖项,如下所示:
<%@ OutputCache duration="60"
VaryByParam="none"
SqlDependency="database_name:table_name;database_name:table_name" %>
配置 SqlCacheDependency 的另一种方法是以编程方式执行此操作。 以下代码在 pubs 数据库中的 authors 表上创建新的 SQL 缓存依赖项。
SqlCacheDependency dep = new SqlCacheDependency("pubs", "authors");
以编程方式定义 SQL 缓存依赖项的好处之一是可以处理可能发生的任何异常。 例如,如果尝试为尚未启用通知的数据库定义 SQL 缓存依赖项,将引发 DatabaseNotEnabledForNotificationException 异常。 在这种情况下,可以通过调用 SqlCacheDependencyAdmin.EnableNotifications 方法并向其传递数据库名称来尝试为通知启用数据库。
同样,如果尝试为尚未启用通知的表定义 SQL 缓存依赖项,将引发 TableNotEnabledForNotificationException 。 然后,可以调用 SqlCacheDependencyAdmin.EnableTableForNotifications 方法,向其传递数据库名称和表名。
以下代码示例演示如何在配置 SQL 缓存依赖项时正确配置异常处理。
try {
SqlCacheDependency SqlDep = new
SqlCacheDependency("pubs", "authors");
} catch (DatabaseNotEnabledForNotificationException exDBDis) {
try {
SqlCacheDependencyAdmin.EnableNotifications("pubs");
} catch (UnauthorizedAccessException exPerm) {
Response.Redirect("ErrorPage.htm");
}
} catch (TableNotEnabledForNotificationException exTabDis) {
try {
SqlCacheDependencyAdmin.EnableTableForNotifications("pubs",
"authors");
} catch (System.Data.SqlClient.SqlException exc) {
Response.Redirect("ErrorPage.htm");
}
} finally {
Cache.Insert("SqlSource", Source1, SqlDep);
}
详细信息: https://msdn.microsoft.com/library/t9x04ed2.aspx
Query-Based SQL 缓存依赖项 (SQL Server 2005 仅限)
将 SQL Server 2005 用于 SQL 缓存依赖项时,不需要基于轮询的模型。 与 SQL Server 2005 一起使用时,SQL 缓存依赖项直接通过 SQL 连接与 SQL Server 实例进行通信, (无需进一步配置) 使用 SQL Server 2005 查询通知。
启用基于查询的通知的最简单方法是以声明方式执行此操作,方法是将数据源对象的 SqlCacheDependency 属性设置为 CommandNotification ,并将 EnableCaching 属性设置为 true。 使用此方法不需要任何代码。 如果针对数据源执行的命令的结果发生更改,则会使缓存数据失效。
以下示例为 SQL 缓存依赖项配置数据源控件:
<asp:SqlDataSource ID="ProductList" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
EnableCaching="true"
SqlCacheDependency="CommandNotification"
SelectCommand="SELECT * FROM [Products]" />
在这种情况下,如果 SelectCommand 中指定的查询返回的结果与最初返回的结果不同,则缓存的结果将失效。
还可以通过将 @ OutputCache 指令的 SqlDependency 属性设置为 CommandNotification 来指定为所有数据源启用 SQL 缓存依赖项。 下面的示例对此进行了说明。
<%@ OutputCache SqlDependency="CommandNotification"
duration="60" VaryByParam="none" %>
注意
有关 SQL Server 2005 中的查询通知的详细信息,请参阅 SQL Server 联机丛书。
配置基于查询的 SQL 缓存依赖项的另一种方法是使用 SqlCacheDependency 类以编程方式执行此操作。 下面的代码示例演示了如何完成此操作。
string sql = "SELECT ProductName, ProductID FROM Products";
SqlConnection conn = new
SqlConnection(ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString);
SqlCommand cmd = new SqlCommand(sql, conn);
SqlCacheDependency dep = new SqlCacheDependency(cmd);
Response.AddCacheDependency(dep);
详细信息: https://msdn.microsoft.com/library/default.asp?url=/library/enus/dnvs05/html/querynotification.asp
缓存后替换
缓存页面可以显著提高 Web 应用程序的性能。 但是,在某些情况下,需要缓存大部分页面,并且页面中的某些片段需要动态。 例如,如果创建的新闻报道页面在设定的时间段内是完全静态的,则可以设置要缓存的整个页面。 如果要包含在每个页面请求中更改的旋转广告横幅,则包含广告的页面部分需要是动态的。 若要允许缓存页面但动态替换某些内容,可以使用 ASP.NET 缓存后替换。 通过缓存后替换,整个页面将输出缓存,特定部分标记为免除缓存。 在广告横幅的示例中,AdRotator 控件允许你利用缓存后替换,以便为每个用户和每个页面刷新动态创建广告。
可通过三种方法实现缓存后替换:
- 以声明方式使用替换控件。
- 以编程方式使用替换控件 API。
- 隐式使用 AdRotator 控件。
替换控件
ASP.NET 替换控件指定动态创建而不是缓存的缓存页的一个部分。 将替换控件放置在页面上要显示动态内容的位置。 在运行时,替换控件调用使用 MethodName 属性指定的方法。 方法必须返回一个字符串,然后替换控件的内容。 方法必须是包含 Page 或 UserControl 控件的静态方法。 使用替换控件会导致客户端的可缓存性更改为服务器可缓存性,因此页面不会缓存在客户端上。 这可确保将来对页面的请求再次调用 方法以生成动态内容。
替换 API
若要以编程方式为缓存页创建动态内容,可以在页面代码中调用 WriteSubstitution 方法,并将方法的名称作为参数传递。 处理动态内容的创建的方法采用单个 HttpContext 参数并返回一个字符串。 返回字符串是将在给定位置替换的内容。 调用 WriteSubstitution 方法而不是以声明方式使用替换控件的优点是,可以调用任意对象的方法,而不是调用 Page 或 UserControl 对象的静态方法。
调用 WriteSubstitution 方法会导致客户端的可缓存性更改为服务器可缓存性,因此页面不会缓存在客户端上。 这可确保将来对页面的请求再次调用 方法以生成动态内容。
AdRotator 控件
AdRotator 服务器控件在内部实现对缓存后替换的支持。 如果在页面上放置 AdRotator 控件,它将在每个请求上呈现唯一的广告,而不考虑是否缓存父页。 因此,包含 AdRotator 控件的页面仅在服务器端缓存。
ControlCachePolicy 类
ControlCachePolicy 类允许使用用户控件对片段缓存进行编程控制。 ASP.NET 在 BasePartialCachingControl 实例中嵌入用户控件。 BasePartialCachingControl 类表示启用了输出缓存的用户控件。
访问 PartialCachingControl 控件的 BasePartialCachingControl.CachePolicy 属性时,始终会收到有效的 ControlCachePolicy 对象。 但是,如果您访问 UserControl 控件的 UserControl.CachePolicy 属性,则仅当用户控件已由 BasePartialCachingControl 控件包装时,您才会收到有效的 ControlCachePolicy 对象。 如果未包装,则属性返回的 ControlCachePolicy 对象在尝试操作它时将引发异常,因为它没有关联的 BasePartialCachingControl。 若要确定 UserControl 实例是否支持缓存而不生成异常,请检查 SupportsCaching 属性。
使用 ControlCachePolicy 类是启用输出缓存的几种方法之一。 以下列表介绍了可用于启用输出缓存的方法:
- 使用 @ OutputCache 指令在声明性方案中启用输出缓存。
- 使用 PartialCachingAttribute 属性为代码隐藏文件中的用户控件启用缓存。
- 在编程方案中,使用 ControlCachePolicy 类指定缓存设置,这些实例使用上述方法之一启用了缓存并使用 System.Web.UI.TemplateControl.LoadControl 方法动态加载的 BasePartialCachingControl 实例。
ControlCachePolicy 实例只能在控件生命周期的 Init 和 PreRender 阶段之间成功操作。 如果在 PreRender 阶段后修改 ControlCachePolicy 对象,ASP.NET 将引发异常,因为在呈现控件后所做的任何更改实际上都不会影响缓存设置, (在呈现阶段) 缓存控件。 最后,用户控件实例 (,因此它的 ControlCachePolicy 对象) 仅在实际呈现时可用于编程操作。
缓存配置更改 - <缓存> 元素
ASP.NET 2.0 中的缓存配置发生了一些更改。 <caching> 元素是 ASP.NET 2.0 中的新增元素,可用于在配置文件中更改缓存配置。 以下属性可用。
元素 | 说明 |
---|---|
缓存 | 可选元素。 定义全局应用程序缓存设置。 |
outputCache | 可选元素。 指定应用程序范围的输出缓存设置。 |
outputCacheSettings | 可选元素。 指定可应用于应用程序中页面的输出缓存设置。 |
sqlCacheDependency | 可选元素。 为 ASP.NET 应用程序配置 SQL 缓存依赖项。 |
缓存<>元素
缓存>元素中<提供了以下属性:
Attribute | 说明 |
---|---|
disableMemoryCollection | 可选的 布尔 属性。 获取或设置一个值,该值指示是否禁用计算机处于内存压力下时发生的缓存内存收集。 |
disableExpiration | 可选的 布尔 属性。 获取或设置一个值,该值指示是否禁用缓存过期。 禁用后,缓存项不会过期,并且不会对过期的缓存项进行后台清理。 |
privateBytesLimit | 可选 Int64 属性。 获取或设置一个值,该值指示缓存开始刷新过期项并尝试回收内存之前应用程序专用字节的最大大小。 此限制包括缓存使用的内存以及正在运行的应用程序的正常内存开销。 设置为零表示 ASP.NET 将使用自己的启发法来确定何时开始回收内存。 |
percentagePhysicalMemoryUsedLimit | 可选 Int32 属性。 获取或设置一个值,该值指示在缓存开始刷新过期项并尝试回收内存之前,应用程序可以使用的最大计算机物理内存百分比。 设置为零表示 ASP.NET 将使用自己的启发法来确定何时开始回收内存。 |
privateBytesPollTime | 可选 TimeSpan 属性。 获取或设置一个值,该值指示轮询应用程序的专用字节内存使用情况之间的时间间隔。 |
<outputCache> 元素
以下属性可用于 <outputCache> 元素。
Attribute | 说明 |
---|---|
enableOutputCache | 可选的 布尔 属性。 启用/禁用页面输出缓存。 如果禁用,则不缓存任何页面,而不考虑编程或声明性设置。 默认值为 true。 |
enableFragmentCache | 可选的 布尔 属性。 启用/禁用应用程序片段缓存。 如果禁用,则不缓存任何页面,而不考虑使用的 @ OutputCache 指令或缓存配置文件。 包含一个缓存控制标头,指示上游代理服务器以及浏览器客户端不应尝试缓存页面输出。 默认值为“false” 。 |
sendCacheControlHeader | 可选的 布尔 属性。 获取或设置一个值,该值指示 缓存控件:private 标头是否默认由输出缓存模块发送。 默认值为“false” 。 |
omitVaryStar | 可选的 布尔 属性。 启用/禁用在响应中发送 Http“Vary: </strong>”标头。如果默认设置为 false,则会为输出缓存页发送“*Vary: *”标头。发送 Vary 标头时,它允许根据 Vary 标头中指定的内容缓存不同的版本。例如,Vary:User-Agents 将基于发出请求的用户代理存储页面的不同版本。默认值为 **false。 |
<outputCacheSettings> 元素
outputCacheSettings <> 元素允许创建缓存配置文件,如前所述。 outputCacheSettings> 元素的唯一子元素<是<用于配置缓存配置文件的 outputCacheProfiles> 元素。
<sqlCacheDependency> 元素
以下属性可用于 <sqlCacheDependency> 元素。
Attribute | 说明 |
---|---|
enabled | 必需的 布尔 属性。 指示是否轮询更改。 |
pollTime | 可选 Int32 属性。 设置 SqlCacheDependency 轮询数据库表更改的频率。 此值对应于连续轮询之间的毫秒数。 不能将其设置为小于 500 毫秒。 默认值为 1 分钟。 |
更多信息
有关缓存配置,应了解一些其他信息。
如果未设置工作进程专用字节数限制,缓存将使用以下限制之一:
- x86 2GB:800MB 或 60% 的物理 RAM,以较小者为准
- x86 3GB:1800MB 或 60% 的物理 RAM,以较小者为准
- x64:1 TB 或 60% 的物理 RAM,以较小者为准
如果同时设置了工作进程专用字节数限制和 <缓存 privateBytesLimit/> ,则缓存将使用这两者中的最小值。
就像在 1.x 中一样,我们删除缓存条目并调用 GC。收集有两个原因:
- 我们非常接近专用字节数限制
- 可用内存接近或小于 10%
通过将 cache percentagePhysicalMemoryUseLimit/> 设置为 <100,可以有效地禁用剪裁和缓存,以降低可用内存条件。
与 1.x 不同,如果最后一个 GC 为,2.0 将暂停剪裁并收集调用。收集未将专用字节或托管堆的大小减少超过 (缓存) 内存限制的 1%。
实验室 1:自定义缓存依赖项
创建新网站。
添加名为 cache.xml 的新 XML 文件,并将其保存到 Web 应用程序的根目录。
将以下代码添加到 default.aspx 代码隐藏中的 Page_Load 方法:
System.Web.Caching.CacheDependency dep = new System.Web.Caching.CacheDependency(Server.MapPath("cache.xml")); Response.AddCacheDependency(dep); Cache.Insert("time", DateTime.Now.ToString()); Response.Write(Cache["time"]);
将以下内容添加到源视图中 default.aspx 的顶部:
<%@ OutputCache Duration="240" VaryByParam="None" %>
浏览 Default.aspx。 时间说什么?
刷新浏览器。 时间说什么?
打开cache.xml并添加以下代码:
<anElement></anElement>
保存cache.xml。
刷新浏览器。 时间说什么?
解释更新时间的原因,而不是显示以前缓存的值:
实验室 2:使用Polling-Based缓存依赖项
本实验室使用在上一模块中创建的项目,该项目允许通过 GridView 和 DetailsView 控件编辑 Northwind 数据库中的数据。
在 Visual Studio 2005 中打开项目。
针对 Northwind 数据库运行 aspnet_regsql 实用工具,以启用数据库和 Products 表。 从 Visual Studio 命令提示符使用以下命令:
aspnet_regsql -S server -ed -d Northwind -E -et -t Products
将以下内容添加到web.config文件:
<caching> <sqlCacheDependency enabled = "true" pollTime = "60000" > <databases> <add name="Northwind" connectionStringName = "Northwind" pollTime = "9000000" /> </databases> </sqlCacheDependency> </caching>
添加名为 showdata.aspx 的新 Web 窗体。
将以下 @ outputcache 指令添加到 showdata.aspx 页:
<%@ OutputCache SqlDependency="Northwind:Products" Duration="480" VaryByParam="None"%>
将以下代码添加到 showdata.aspx 的Page_Load:
Response.Write(DateTime.Now.ToString() + "<br><br>");
将新的 SqlDataSource 控件添加到 showdata.aspx,并将其配置为使用 Northwind 数据库连接。 单击“下一步”。
选中“ProductName”和“ProductID”复选框,然后单击“下一步”。
单击“完成”。
将新的 GridView 添加到 showdata.aspx 页。
从下拉列表中选择“SqlDataSource1”。
保存并浏览 showdata.aspx。 记下显示的时间。