6. 缓存 - 《APS.NET本质论》
CaChe是ASP.NET中唯一可以根据服务器使用情况,动态管理内存使用的状态管理方案。我们通过每个缓存数据的键值字符串来区分缓存的数据。
简单案例来说。将数据从数据库/文件取出放在服务器内存中,后来的用户获取数据,不用查询数据而直接从内存中获取,提高了访问速度,减轻服务器的压力。——经常查询,不经常改动。
题外话,分布式缓存:Memcache,Redis等。(现在的大数据项目需要用到这些)
1.1 缓存的原理
缓存需要注意以下几点
- 缓存的空间是有限的。
- 缓存的内容可能会失效,原因:
- 绝对过期。在特定的时间点后,缓存数据失效,例如天气预报。
- 活动过期。在一个时间间隔之内,如果数据没有使用,那么,数据将过期。
- 缓存依赖。
缓存数据依赖于某个对象,例如文件系统中的文件或者文件夹,数据库中的数据等,当被依赖的对象发生变化,缓存的数据将成为脏数据。
- 优先级。当缓存空间不够的时候,其他条件相同的情况下,通过优先级判断哪些数据需要从缓存中跑起掉。
- 通过移除通知,当缓存的数据被从缓存中移除的时候,可以通知应用程序。应用程序需要写一个符合特定委托的方法,以便接收通知。
- 由于内存有限,所以不太可能将所有数据缓存到内存中,因此,Cache 必须对缓存的数据进行有效的调度,以达到内存使用效率的最大化。
1.2 .NET 中缓存管理实现
在.NET中缓存的定义命名空间是 System.Web.Cachine 中
- Dependency 依赖:
- .NET 中支持两种依赖:CacheDependency 和 SqlDependency。从.NET 2.0 开始,CacheDependency 不再密封,而是可以继承。
CacheDependency :表示对文件或目录的依赖。
SqlDependency :表示对 SQL 数据库的依赖。
- 过期时间:过期时间分为绝对过期时间和滑动过期时间。(只能二选一)
绝对过期时间为一个特定的时间点,类型是 DateTime,如果不使用绝对过期时间,那么,使用 System.Web.Caching.Cache.- 绝对过期时间为一个特定的时间点,类型是 DateTime,如果不使用绝对过期时间,那么,使用 System.Web.Caching.Cache.NoAbsoluteExpiration 表示。
- 滑动过期时间为一个时间间隔,类型为 TimeSpan,如果不使用滑动过期时间,使用 System.Web.Cachine.NoSlidingExpiration 表示。
- 优先级 CacheItemPrority:
由于我们需要缓存大量的数据,在内存有限的情况下,就必须对缓存的数据进行优先级分类,重要的数据可以更长时间保存在内存中。CacheItemPriority 是一个枚举类型,从高到低定义了如下优先级:
- NotRemoveable,最高优先级,不会因为内存调整被移除,除非过期或者缓存依赖被清除。
- High,服务器释放内存时,最不可能被移除
- AboveNormal,被移除的可能性比 Normal 小
- Normal,具有该级别的数据很可能被移除。
- Default,就是 Normal 级别
- BelowNormal,具有该优先级的数据比 Normal 级别更有可能移除。
- Low,此级别的数据最可能被移除。
- 删除通知:
.NET 提供了一个机制,当被缓存的数据从内存中移除的时候,可以提供一个通知机制,来回调用户定义的方法,方法必须符合 CacheItemRemovedCallback 委托的定义。
public delegate void CacheItemRemovedCallback {
string key,
Object value,
CacheItemRemovedReason reason
}
其中, CacheItemRemovedReason 未婚村被移除的原因。CacheItemRemovedReason 是一个枚举类型,
定义了如下原因:DependencyChanged,由于依赖发生变化被移除。
Expired,由于过期被移除。
Removed,由于调用 Insert 插入一个同名的缓存项或者调用 Remove 失效被移除。
Underused,系统移除。
- 特别注意:
回调的时机是不可预测的,不能假定回调发生时,回调方法的执行线程存在 HttpContext 的上下文,为了在没有请求上下文的时候取得对 Cache 对象的引用,可以通过 HttpRuntime 的 Cache 属性来使用应用程序的 Cache 。
- 不能在页面上使用实例方法来作为回调方法,当在页面上使用回调方法时,由于指向回调方法的引用会阻止垃圾回收机制,因此会造成内存很快消耗光。
- 一般通过在自定义类的静态方法实现回调方法,或使用页面对象的静态方法实现。
1.3.基于文件的缓存依赖
CacheDependency 表示依赖对于文件或者目录的依赖。
Public class CacheDependency : Idisposable
通过改变文件的更新日期来清除缓存。
public static string Message
{
get {
HttpContext context = System.Web.HttpContext.Current;
string message = context.Cache["Message"] as string;
if (message == null)
{
string path = context.Server.MapPath("~/TextFile.txt");
message = System.IO.File.ReadAllText(path);
context.Cache.Add(
"Message",
message,
new CacheDependency(path),
Cache.NoAbsoluteExpiration,
new TimeSpan(1, 0, 0),
CacheItemPriority.Normal,
CallBack
);
}
return message;
}
}
/// <param name="reason">原因</param>
private static void CallBack(string key, Object value, CacheItemRemovedReason reason)
{
//但是需要注意的是,此时不一定有请求
//因此,不能使用 HttpContext
//如果需要使用 Cache
//可以通过 System.Web.HttpRuntime.Cache 来获取当前网站应用程序的 Cache
}
1.4 基于 SQL 的缓存依赖
当数据库信息变化时,应用程序获取变化的通知是缓存依赖得以实现的基础。
- 数据库通知:
当数据库中信息变化,主动通知应用程序。
- 轮询:
数据库不能通知的时候,应用程序主动定期访问数据库,检查数据变化。
微软从 SQL Server 2005开始提供数据库通知。如果数据库不支持通知机制,那么只能通过轮询来实现。
但是轮询不可能再次查询一遍进行比较,我们通常通过触发器来实现。
轮询的方法:
sqlCacheDependency 的 enabled 属性为真,表示启动轮询,pollTime,毫秒为单位,意识是每隔2秒检测下数据库,检测表是否有发生变化。connectionStringName为数据库链接字符串。
<connectionStrings>
<add name="Am_WeixinWeb" connectionString="data source=192.168.1.200;initial catalog=Am_WeixinWeb;uid=sa;password=lh1234;" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true" pollTime="2000">
<databases>
<add name="Test" connectionStringName="Am_WeixinWeb" />
</databases>
</sqlCacheDependency>
</caching>
SqlCacheDependency 表示 SQL 缓存依赖对象,即支持通知机制也可以支持轮询机制。
创建 SqlCacheDependency 对象的方法如下:
SqlCacheDependency dep = new SqlCacheDependency("Test", "Am_recProScheme"); //Test对应配置项的缓存配置key ,后面是数据库表名
如果使用 SQL 2005 以及后面的版本,可以使用通知机制。
Alter database 数据库名称 set enable_broker
然后,定义一个有效的查询对象来获取数据库的查询通知。SqlCommand 必须满足如下要求:
表名必须包括所有者名称的完全限定名;
Select 中 显式指定列名,不能使用 * 来选择所有列。
SqlCommand command = new SqlCommand(connectionString,sql);
最后,构造 SQL 缓存依赖对象,
SqlCacheDependency scd = new SqlCacheDependency (command);
如果使用通知机制,不需要使用工具在数据库中做准备工作,也不需要在配置文件中设置轮询。
1.5 组合的缓存依赖
当依赖的内容为多个 Denpendency (可能是 CacheDependency 或者 SqlDependency)时,可以通过 AggregateCacheDependency 创建依赖的组合,在任何一个依赖项变化时,使缓存失效。
1.6 删除所有缓存项目
Cache 中没有内建清除全部项目的方法。所以必须遍历这个集合,去的其中的 Key 的集合,然后再遍历所有的 Key ,使用 Key 删除缓存项目。
1.7 Web 服务器端的页面缓存
1.8 页面局部缓存
页面部分缓存是将页面部分内容保存在内存中以便响应用户请求。两种:
1.用户控件缓存
在用户控件中的 OutputCache 指令支持选项:
- Duration
- VaryByParam
- VaryByControl
- VaryByCustom
- VaryByContentEncodings
- Shared
- SqlDenpendcy
2.缓存后替换
大部分内容需要缓存,某些片断内容是动态的。
例如新闻页面,新闻内容大部分时间不变,但是页面显示的当前时间、广告信息等需要随时变化。
缓存后替换:将整个页面输出缓存,将特定部分标记为不缓存。
三种方式:
- 声明的方式使用 Substitution 控件
- 编程的方式使用 Substitution 控件
- 使用 Adrotor 控件
* 1.9 自定义的输出缓存提供器
.NET 4.0 之前使用 System.Web.Caching.Cache,4.0 之后重新进行了设计,提供一个 OutputCacheProvider 供开发人员扩展。默认情况,还是使用 System.Web.Caching.Cache。
题外:2.1 Memcached 分布式内存对象缓存系统
自己搜索。
参考:
- http://www.cnblogs.com/knowledgesea/p/3904929.html asp.net缓存 - 张龙豪
- 《ASP.NET 本质论》郝冠军