当决定是否缓存一个对象时,关于数据的格式和访问机制,你需要考虑三个主要问题:
1、 线程安全——当缓存的内容可以被多个线程访问时,使用某种锁定机制来保证数据不会被两个线程同时操作;
2、 序列化——将一个对象缓存时,需要将它序列化以便保存,所以包缓存的对象必须支持序列化;
3、 规格化缓存数据——缓存数据时,相对于要使用的数据格式而言,要保证数据的格式是优化过的。
在使用缓存数据前,必须将数据加载到缓存中,有两种机制来加载数据:
·提前加载Proactive Load——使用这种方式时,你提前将所有的状态数据加载到缓存中,可能在应用程序或线程启动时进行,然后在应用程序或线程的生存期内一直缓存;
·动态加载Reactive Load——或称反应式加载,当使用这种方法时,在应用程序请求数据时取到数据,并且将它缓存起来以备后续使用。
另外一个关键因素是如何保持缓存数据和主数据(文件或数据库或其他的应用程序资源)的一致性,你可以定义过期策略来决定缓存中的内容,如已经缓存的时间或者收到其他资源的通知。
当缓存数据时,需要非常清楚缓存中数据的潜在安全威胁。缓存中的数据可能会被别的进程访问或修改,而此进程对主数据是没有权限的。原因是当数据存储在原始位置时,有相应的安全机制来保护它,当数据被带出传统的安全边界时,需要有同等的安全机制。
当你缓存数据时,应用系统需要的维护工作加大了。在发布应用程序时,需要配置相应的属性,比如缓存的大小限制和清除策略。同时要使用某种机制来监控缓存的效率(比如事件日志和性能计数器)
第一节内容简单介绍了缓存技术中的概念、缓存数据的原因和方案、优势、实施缓存方案时的考虑等基本内容。现在你对缓存中涉及的内容有了一个大致了解,下面着重介绍可用的缓存技术。
本节将介绍以下技术:
使用Asp.Net缓存;
使用Remoting Singleton缓存;
使用内存映射文件;
使用SQL Server缓存;
使用静态变量缓存;
使用Asp.net 会话状态(Session State);
使用Asp.net客户端缓存和状态;
使用Internet Explorer缓存。
将常用的数据保存在内存中对asp的开发人员来说并不陌生,Session对象和Application对象提供键值对来缓存数据,Session对象保存和单个用户有关的数据,Application对象可保留和应用程序有关的数据,每个用户都可以访问。
在Asp.net中,提供了专门用于缓存数据的Cache对象,它的应用范围是应用程序域。生存期是和应用程序紧密相关的,每当应用程序启动的时候就重新创建Cache对象。它域Application对象的主要区别就是提供了专门用于缓存管理的特性,比如依赖和过期策略。
你可以使用Cache对象和它的属性来实现高级的缓存功能,同时可以利用Asp.net Cache来对客户端输出的响应内容进行缓存。关于Asp.net中的缓存技术,有以下内容要介绍:
Cache对象定义在System.Web.Caching命名空间,可以使用HttpContext类的Cache属性或Page对象的Cache属性来得到Cache的引用,Cache对象除了存储键值对以外,还可以存储.net框架的对象。下面介绍相应的依赖和过期策略。
当向缓存中加数据时,可以指定它的依赖关系来实现在某些情况下强制移除它。可用的方案包括以下几种:
·文件依赖(File Dependency)——当硬盘上的某个(某些)文件更改时,强制移除缓存数据;如:
CacheDependency cDependency = new
CacheDependency(Server.MapPath("authors.xml"));
Cache.Insert("CachedItem", item, cDependency);
·键值依赖(Key Dependency)——指定缓存中的某个数据项更改时移除。如:
// Create a cache entry.
Cache["key1"] = "Value 1";
// Make key2 dependent on key1.
String[] dependencyKey = new String[1];
dependencyKey[0] = "key1";
CacheDependency dependency = new CacheDependency(null, dependencyKey);
Cache.Insert("key2", "Value 2", dependency);
·基于时间的过期策略——按照预先定义的时间策略来使数据失效,可以是绝对时间(如某个日期的18:00)也可以是相对现在的相对时间。如:
/// Absolute expiration
Cache.Insert("CachedItem", item, null, DateTime.Now.AddSeconds(5),Cache.NoSlidingExpiration);
/// Sliding expiration
Cache.Insert("CachedItem", item, null, Cache.NoAbsoluteExpiration,
TimeSpan.FromSeconds(5));
使用太短和太长的过期时间都不行,不是造成用不上的缓存数据,就是缓存了陈旧的数据并加重了缓存负担,所以可以使用高并发的测试来决定过期时间的最佳值。
·另外有个问题就是如何实现对数据库的依赖,这就要求实现自己的通知机制,当数据库数据改变时能够通知你的缓存数据改变。可参考http://www.gotdotnet.com/team/rhoward的示例。
由于数据会过期,所以当使用缓存中的数据时,必须检查数据的有效性。如以下代码:
string data = (string)Cache["MyItem"];
if (data == null)
{
data = GetData();
Cache.Insert("MyItem", data);
}
DoSomeThingWithData(data);
依赖和过期策略指定了缓存中数据的移除方式,有时候你可能需要在移除发生时做一些工作,这能靠写代码来实现这一点,这就是我们要讲到的。
你可以定义回调,这样当移除自动发生时, 你可以不移除它或者使用新的数据来替换它。如:
CacheItemRemovedCallback CacheItemRemovedCallback(this.RemovedCallback);
Cache.Insert("CachedItem",
item,
null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
onRemove);
// Implement the function to handle the expiration of the cache.
public void RemovedCallback(string key, object value, CacheItemRemovedReason r)
{
// Test whether the item is expired, and reinsert it into the cache.
if (r == CacheItemRemovedReason.Expired)
{
// Reinsert it into the cache again.
CacheItemRemovedCallback >onRemove = new CacheItemRemovedCallback(this.RemovedCallback);
Cache.Insert(key,
value,
null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
onRemove);
}
}
当运行应用程序的服务器内存不足时,会自动清除缓存中的数据,称为“清除scavenging”。此时,Cache对象根据缓存项的优先级来决定先移除哪些缓存数据,你可以在代码中指定缓存项的优先级。参看MSDN中“CacheItemPriority 枚举”,如:
Cache.Insert("DSN", connectionString, null, d, t, CacheItemPriority.High, onRemove);
没有直接的方法来刷新Asp.net的输出缓存,但是有替代方法(设置所有数据失效),比如:
Response.Cache.SetExpires(DateTime.Now)
这可以清除缓存,但页面上并不立刻体现出来,直到最初的缓存期结束,比如:
<%@ OutputCache Duration="10" VaryByParam="none" %>指令指定的缓存只会在10秒后才清除。通常并不需要清除所有缓存项,你只要重新加载数据更新缓存就够了。
你可以使用两种方式的输出缓存来缓存需要传输和显示到客户端浏览器上的数据——页面输出缓存(Page Output Cache)和页面片断缓存(Page Fragment Cache)。当整个页面相对变化较少时,可以缓存整个页面;如果只是页面的一部分经常变化,可以使用片断缓存。