1 概念 · 稳定性——同一个应用中,对同一数据、逻辑功能和用户界面的多次请求时经常发生的。当用户基数很大时,如果每次请求都进行处理,消耗的资源是很大的浪费,也同时造成系统的不稳定。例如,web应用中,对一些静态页面的呈现内容进行缓存能有效的节省资源,提高稳定性。而缓存数据也能降低对数据库的访问次数,降低数据库的负担和提高数据库的服务能力; · 可用性——有时,提供数据信息的服务可能会意外停止,如果使用了缓存技术,可以在一定时间内仍正常提供对最终用户的支持,提高了系统的可用性。 1.2 理解状态 1.2.1 状态的生存期 ·永久状态Permanent State——应用程序使用的永久数据; ·进程状态Process State——只在进程周期内有效; ·会话状态Session State——和特定的用户会话有关; ·消息状态Message State——处理某个消息的时间内有效; 1.2.2 状态的范围 ·物理范围指可以被访问到的状态数据存放的物理位置,通常包括: 1、 组织Organization——在一个组织内的所有应用程序可以访问状态数据; 2、 场Farm——在应用场范围内的任何机器上都可以访问; 3、 机器Machine——单个机器范围内可以访问; 4、 进程Process——进程内的访问许可; 5、 应用域AppDomain——应用程序域内的访问许可。 ·逻辑范围指可访问状态数据的逻辑范围,常见的有: 1、 应用程序Application; 2、 业务流程Business Process; 3、 角色Role; 4、 用户User; 1.2.3 状态数据的陈旧 ·主数据更改的可能性——随着时间的推进,主数据更改的可能是否大大增加?安照这一点来决定缓存状态数据的陈旧; ·更改的相关性——主数据更新时,缓存的状态数据不相应更新是不是造成影响系统的使用?比如,更改系统的外观风格并不会对业务造成很大影响。 1.2.4 状态数据陈旧的容忍度 1.2.5 理解状态数据的转换过程 表现形式 当决定缓存数据时,应该考虑缓存哪个阶段(哪种形式)的状态数据。以下方针有助于你做决定: · 当业务逻辑可以容忍缓存数据的陈旧时就缓存原始数据;原始数据可以缓存在数据库访问组件和服务代理中; ·缓存处理过的数据以减少处理时间和资源;处理过的数据可以缓存在业务逻辑组件和服务接口中。 ·当需要呈现的数据量很大并且控件的呈现时间很长时,缓存呈现数据(比如包含大数据量的Treeview控件)。这种数据应该被缓存在UI控件中。 1.3 为什么要缓存数据 ·减少交互的通讯量——缓存数据能有效减少在进程和机器间的传输量; ·降低系统中的处理量——减少处理次数; ·降低需要做的磁盘访问次数——比如缓存在内存中的数据。 1.4 数据应该被缓存在哪里 1、 存储类型Storage Type——数据可用的物理存储位置; 2、 层间的架构元素(Layered architecture elements)——数据可用的逻辑存储位置。 1.4.1 存储类型 1、 内存驻留缓存——包含在内存中临时存储数据的所有实现方法,通常在以下情况下使用: a) 应用程序频繁使用同样的数据; b) 应用程序需要经常获取数据; 通过将数据保留在内存中,你可以有效降低昂贵的磁盘访问操作,也可以通过将数据保留在使用者进程中来最大程度的减少跨进程的数据传输。 2、 磁盘驻留缓存——这种技术包含所有使用磁盘作为存储介质的缓存技术,如文件和数据库。在以下情况下基于磁盘的缓存是很有效的: a) 处理大数据量时; b) 应用服务提供的数据可能并不是总能使用(比如离线的情况); c) 缓存的数据必须能在进程回收和机器重启的情况下保持有效; 通过缓存处理过的数据,你可以有效降低数据处理的负担,同时可减少数据交互的代价。 1.4.2 架构间元素 当使用这些组件进行工作时,你需要考虑哪些数据可以被缓存起来,还有以哪种方式进行缓存会对程序的整体性能和可用性有帮助,以上的这些元素都可以缓存相应的数据。当然,要考虑的远不止这些。 1.5 实施缓存时的考虑 1.5.1 格式和访问模式 1、 线程安全——当缓存的内容可以被多个线程访问时,使用某种锁定机制来保证数据不会被两个线程同时操作; 2、 序列化——将一个对象缓存时,需要将它序列化以便保存,所以包缓存的对象必须支持序列化; 3、 规格化缓存数据——缓存数据时,相对于要使用的数据格式而言,要保证数据的格式是优化过的。 1.5.2 内容加载 ·提前加载Proactive Load——使用这种方式时,你提前将所有的状态数据加载到缓存中,可能在应用程序或线程启动时进行,然后在应用程序或线程的生存期内一直缓存; ·动态加载Reactive Load——或称反应式加载,当使用这种方法时,在应用程序请求数据时取到数据,并且将它缓存起来以备后续使用。 1.5.3 过期策略 1.5.4 安全性 1.5.5 管理 1.6 小结 2 缓存技术 使用Asp.Net缓存; 使用Remoting Singleton缓存; 使用内存映射文件; 使用SQL Server缓存; 使用静态变量缓存; 使用Asp.net 会话状态(Session State); 使用Asp.net客户端缓存和状态; 使用Internet Explorer缓存。 2.1 Asp.net缓存 在Asp.net中,提供了专门用于缓存数据的Cache对象,它的应用范围是应用程序域。生存期是和应用程序紧密相关的,每当应用程序启动的时候就重新创建Cache对象。它域Application对象的主要区别就是提供了专门用于缓存管理的特性,比如依赖和过期策略。 你可以使用Cache对象和它的属性来实现高级的缓存功能,同时可以利用Asp.net Cache来对客户端输出的响应内容进行缓存。关于Asp.net中的缓存技术,有以下内容要介绍: 2.1.1 编程缓存Programmatic Caching 2.1.1.1 依赖和过期策略 ·文件依赖(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); 依赖和过期策略指定了缓存中数据的移除方式,有时候你可能需要在移除发生时做一些工作,这能靠写代码来实现这一点,这就是我们要讲到的。 2.1.1.2 使用缓存回调(Cache Callback) CacheItemRemovedCallback onRemove = new 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 = null; onRemove = new CacheItemRemovedCallback(this.RemovedCallback); Cache.Insert(key, value, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, onRemove); } } 2.1.1.3 对缓存项使用优先级 Cache.Insert("DSN", connectionString, null, d, t, CacheItemPriority.High, onRemove); 2.1.1.4 刷新数据(清除缓存) Response.Cache.SetExpires(DateTime.Now) 这可以清除缓存,但页面上并不立刻体现出来,直到最初的缓存期结束,比如: <%@ OutputCache Duration="10" VaryByParam="none" %>指令指定的缓存只会在10秒后才清除。通常并不需要清除所有缓存项,你只要重新加载数据更新缓存就够了。 2.1.2 输出缓存(Output Cache) 2.1.2.1 页面输出缓存 1、 决定缓存的内容 页面输出缓存可缓存各种信息,缓存这些信息意味着你不需要经常处理同样的数据和结果,包括: ·经常被请求但不不改变的静态页面; ·更新频率和时间已知的页面(如显示股票价格的页面); ·根据HTTP参数,有几个可能输出的页面(如根据城市的代号显示该城市天气情况的页面); ·从Web Service返回的结果;如: [WebMethod(CacheDuration=60)] public string HelloWorld() { return "Hello World"; } 2、 缓存动态页面 基于输入参数、语言和浏览器类型改变的动态网页经常用到。你可以使用OutputCache的以下属性来实现对动态页面的缓存: VaryByParam——基于输入参数不同缓存同一页面的多个版本; VaryByHeader——基于Page Header的内容不同缓存页面的多个版本; VaryByCustom——通过声明属性和重载GetVaryByCustomString方法来定制缓存处理页面的多个版本; VaryByControl——基于控件中asp对象属性的不同来缓存控件。 对多个版本页面的缓存会降低可用内存,所以要仔细衡量缓存策略。s 3、 控制缓存的位置 你可以使用@OutputCache指令的OutputCacheLocation属性的枚举值来指定缓存的位置,如: <%@ outputcache duration="10" varybyparam="none" Location="Server" %> 4、 配置页面输出缓存 有两种方式控制,你可以使用Page指令,也可以使用Cache API编程实现。参考以下两段代码: //代码1,使用指令 <%@ OutputCache Duration="20" Location="Server" VaryByParam="state" VaryByCustom="minorversion" VaryByHeader="Accept-Language"%> //代码2,编程实现 private void Page_Load(object sender, System.EventArgs e) { // Enable page output caching. Response.Cache.SetCacheability(HttpCacheability.Server); // Set the Duration parameter to 20 seconds. Response.Cache.SetExpires(System.DateTime.Now.AddSeconds(20)); // Set the Header parameter. Response.Cache.VaryByHeaders["Accept-Language"] = true; // Set the cached parameter to 'state'. Response.Cache.VaryByParams["state"] = true; // Set the custom parameter to 'minorversion'. Response.Cache.SetVaryByCustom("minorversion"); … } 2.1.2.2 页面片断缓存 ·创建开销很大的页面片断(控件); ·包含静态数据的页面片断; ·可被多个用户使用的页面片断; ·多个页面共享的页面片断(如公用菜单条) 以下是缓存部分页面的例子: // Partial caching for 120 seconds [System.Web.UI.PartialCaching(120)] public class WebUserControl : System.Web.UI.UserControl { // Your Web control code } 2.1.3 在非Web项目中使用Asp.net缓存 System.Web.Caching.Cache 类是对象的缓存,它可以通过System.Web.HttpRuntime.Cache 的静态属性或System.Web.UI.Page 和System.Web.HttpContext.Cache来访问。因此在请求上下文之外也可以存在,在每个应用程序域中只有一个实例,所以HttpRuntime.Cache对象可以在Aspnet_wp.exe之外的每个应用程序域中存在。以下代码演示了在普通应用里访问Cache对象: HttpRuntime httpRT = new HttpRuntime(); Cache cache = HttpRuntime.Cache;
2.2 使用Remoting Singleton缓存 为了使用.Net Remoting实现缓存方案,要保证远程对象的租约不过期,并且远程对象没有被垃圾回收器销毁(对象租约是指在系统删除该对象前它在内存中的生存期)。当实现缓存时,重载MarshalByRefObject的InitializeLifetimeService方法并且返回null,这样就能保证租约永远不过期并且相关的对象生存期是无限的。以下代码是一个示例: public class DatasetStore : MarshalByRefObject { // A hash table-based data store private Hashtable htStore = new Hashtable(); //Returns a null lifetime manager so that GC won't collect the object public override object InitializeLifetimeService() { return null; } // Your custom cache interface } 注意:由于这种方案的成本较高、性能上的限制并且可能造成系统不稳定,通常采用基于Sql Server的方案来替代。 2.3 使用内存映射文件(Memory-Mapped File) 在windows中,代码和数据是以以种方式处理的,表现形式都是内存页,而在内存页背后都是磁盘上的文件。唯一的不同磁盘上的文件类型不同。代码后面是可执行的镜像,而数据后面则是系统的页面文件。当多个应用程序共享内存时,系统的性能会有明显提升。 你可以使用内存映射文件的这种特性来实现同一台机器上的跨进程和跨应用程序域的缓存解决方案。基于内存映射文件的缓存方案包含以下组件: ·windows NT服务——启动时创建内存映射文件,停止时删除它。功能是向使用缓存的进程提供句柄。当然,也可以使用命名的内存映射文件来提供操作接口; ·缓存托管组件(Cache Management Dll)——实现特定的缓存功能,比如: a. 插入和删除数据项到缓存中; b. 使用算法清除缓存,比如最后使用算法(Least Recently Used); c. 保证数据不被篡改; 基于内存映射文件的缓存方案可以用在应用程序的每个层中,但由于使用win32 API调用,所以并不容易实现。.Net 框架不支持内存映射文件,所以只能以非托管代码的方式运行,当然也不能利用.Net框架的有力特性,比如垃圾回收等。同时缓存数据项的管理功能需要定制开发,还要开发性能计数器来监控缓存的效果。 2.4 使用SQL Server缓存 SQL Server在使用sql语句或存储过程得到数据时,对varchar和varBinary类型的数据有8k的大小限制,你必须使用.Net 框架提供的Ado.Net SQLDataAdapter对象来访问datatable或dataset。 使用SQL Server缓存数据的优点: ·易于实现——使用.Net 框架和Ado.Net访问数据库相当方便; ·完善的安全模型和很高的健壮性; ·数据非常方便的共享; ·数据的持久保留。 ·支持很大的数据量。 ·方便的管理工具 当然,也有缺点: ·需要安装SQL Server,对小型应用来说不合适; ·重新构造数据的性能和读取数据库的性能比较; ·网络负担。 2.5 使用静态变量缓存 你可以使用这种方案保存大数据的对象,前提是它不经常更改。由于没有清除机制,大数据的内存消耗会影响性能。 你需要保证定制线程安全机制,或者使用.Net框架提供的同步对象,比如Hashtable。以下代码是使用Hashtable实现的例子: static Hashtable mCacheData = new Hashtable(); 应用范围:本方案的应用范围可以限制到类、模块或整个项目。如果变量定义为public,整个项目中的代码都能访问它,范围是整个应用程序域,实现了高效的共享。而它的生存期是和范围紧密相关的。 2.6 使用asp.net session state ·asp session要求客户端接受cookies,否则就不能使用session;而asp.net可以配置为不使用cookie; ·对web server场的情况,asp的session不能支持;当稳定性和可用性要求很高时,asp.net session state虽然效果不好,但对比较小的单个值scalar Value(比如登录信息),还是很有效。 Asp.net session有很大改进,下面描述使用范围和使用方式。 Asp.net session state有三种操作模式: 1、 进程内模式InProc——Session State信息在asp.net工作进程aspnet_wp.exe的进程的内存中存储。这是默认选项,这种情况下,如果进程或应用程序域被回收,则Session 状态信息也被回收; 2、 进程外模式State Server——状态信息序列化后保存在独立的状态进程中(AspNet_State.exe),所以状态信息可以保存在专门的服务器上(一个状态服务器State Server); 3、 Sql server模式——状态信息序列化后保存在SQL Server数据库中。 你可以通过调整配置文件中<sessionState>标签的mode属性来设置要使用的状态模式,比如使用SQL Server模式来在Web server场中共享状态信息。当然,这个优势也有缺点,就是状态信息需要序列化和反序列化,同时多了对数据库的写入和读取,所以性能上有开销,这是要仔细评估的。 2.6.1 选择使用模式 进程内模式是唯一支持Session_End事件的session模式,当用户会话超时或中止时,可以运行Session_End中的事件处理代码来清除资源。 2.6.1.2 使用StateServer模式 当使用Session对象在web场的情况下使用时,必须保证web.config文件中的<MachineKey>元素在所有服务器上是唯一的。这样所有的服务器使用同样的加密方式,才能访问缓存中的数据。参考msdn中的“MachineKey元素”。 2.6.1.3 使用SQL Server模式 默认情况下,SQL Server将状态信息存储在TempDb数据库中,它在每次Sql server服务启动时会自动重新创建,当然,你可以指定自己的数据库以便在数据库重启的过程中也能保持数据。 2.6.2 决定使用Session对象要存储的内容 1、 对基本类型(比如Int,Byte,String)来说,可以使用任何方式。因为在选用进程外方式时,asp.net使用一个优化的内部方法来序列化和反序列化基本类型的数据; 2、 对复杂类型(如ArrayList)来说,只选用进程内方式。因为asp.net使用BinaryFormatter来序列化和反序列化这类数据,而这会影响性能的。当然,只有在State Server和SQL Server的方式下,才会进行序列化操作; 3、 缓存的安全问题,当在缓存中存储敏感数据时,需要考虑安全性,其它页面可以访问到缓存中的数据; 4、 避免缓存大数据,那会降低性能; 5、 这种缓存方式不支持过期策略、清除和依赖。 2.6.3 实现Session State 以下代码演示了使用SQL Server来实现Session数据的存储和使用。 <sessionState mode="SQLServer" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1; Integrated Security=SSPI" cookieless="false" timeout="20" /> private void SaveSession(string CartID) { Session["ShoppingCartID"] = CartID; } private void CheckOut() { string CartID = (string)Session["ShoppingCartID"]; if(CartID != null) { // Transfer execution to payment page. Server.Transfer("Payment.aspx"); } else { // Display error message. } } 2.7 使用Asp.net客户端缓存和状态 实现客户端缓存的机制有以下五种,接下来将依次介绍: ·隐藏栏位(Hidden Field) ·View State ·隐藏帧(Hidden Frame) ·Cookies ·Query String 这五种方式分别适合于存储不同类型的数据。 2.7.1 使用Hidden Field 使用这种方式的优点如下: 不需要服务器资源,直接从页面中读取; 由于可以通过查看源码看到,所以可能会被篡改; <input id="HiddenValue" type="hidden" value="Initial Value" runat="server" NAME="HiddenValue"> 2.7.2 使用View State 使用View State的性能表现很大程度上依赖于服务器控件的类型。一般来说,Label,TextBox,CheckBox,RadioButton,HyperLink的性能要好一些,而DropdownList,ListBox,DataGrid和DataList就要差很多,因为包含的数据量太大,所以每次页面回送都很耗时间。 有些情况下不推荐使用ViewState,比如: 1、 不需要回送的页面避免使用; 2、 避免使用ViewState保存大数据量; 3、 在需要使用会话超时的情况下避免使用它,因为它没有超时操作。 ViewState的性能表现和Hidden Field的是类似的,但是具有更高的安全性。 优点: 数据在页面中自动维护,不需要服务器资源; 存储大数据量时会降低性能; public class ViewStateSample : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { if (!Page.IsPostBack) { // Save some data in the ViewState property. this.ViewState["EnterTime"] = DateTime.Now.ToString(); this.ViewState["UserName"] = "John Smith"; this.ViewState["Country"] = "USA"; } } … private void btnRefresh_Click(object sender, System.EventArgs e) { // Get the saved data in the view state and display it. this.lblTime.Text = this.ViewState["EnterTime"].ToString(); this.lblUserName.Text = this.ViewState["UserName"].ToString(); this.lblCountry.Text = this.ViewState["Country"].ToString(); } } 2.7.3 使用Hidden Frame 优点: a. 可以加载较多数据而不只是单个栏位的值; b. 避免了不必要的多次回送中的数据往来; c. 可以缓存和读取在不同表单中存储的数据项(可以同时缓存多个页面的数据); d. 可以访问同一站点不同frame中的客户端脚本数据。 缺点: a. 有些浏览器不支持frame; b. 源代码可以在客户端看到,有潜在的安全威胁; c. 隐藏frame的数量没有限制,如果框架页面包含较多hidden frame的话,在首次加载时速度会有限制。 示例代码如下: <FRAMESET cols="100%,*"> <FRAMESET rows="100%,*"> <FRAME src="contents_of_frame1.html"> </FRAMESET> <FRAME src="contents_of_hidden_frame.html"> <FRAME src="contents_of_hidden_frame.html" frameborder="0" noresize scrolling="yes"> <NOFRAMES> <P>This frameset document contains: <A href="contents_of_frame1.html" TARGET="_top">Some neat contents</A> </NOFRAMES> </FRAMESET> 2.7.4 使用Cookies 优点: 不需要服务器资源;数据保存在客户端,在用户请求时发送到服务器上。 数据量的限制; public class CookiesSample : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { if (this.Request.Cookies["preferences1"] == null) { HttpCookie cookie = new HttpCookie("preferences1"); cookie.Values.Add("ForeColor","black"); cookie.Values.Add("BackColor","beige"); cookie.Values.Add("FontSize","8pt"); cookie.Values.Add("FontName","Verdana"); this.Response.AppendCookie(cookie); } } private string getStyle(string key) { string val = null; HttpCookie cookie= this.Request.Cookies["preferences1"]; if (cookie != null) { val = cookie.Values[key]; } return val; } } 2.7.5 使用Query String 优点: d. 不需要服务器资源,参数附在URL里面; e. 应用面广,几乎所有浏览器都支持; f. 实现简单,服务端使用Request对象可直接读取。 缺点: a. 参数直接对用户可见,不安全; b. URL长度的限制,多数浏览器不支持超过255字符的URL。 示例代码: http://www.cache.com/login.asp?user=ronen string user = Request.QueryString["User"]; 2.7.6 小结 缓存机制
2.8 使用Internet Explorer缓存 适合在Internet Explorer中缓存的内容 页面中的图像文件; 减少对服务器的请求和网络负担; 客户端的过期时间必须预先指定而不能依赖于服务器更新;IE采用的是Lazy更新机制,优先从缓存中提取数据; <META HTTP-EQUIV="expires" CONTENT="Tue, 23 Jun 2002 01:46:05 GMT"> 3 总结
|
缓存技术探讨
最新推荐文章于 2025-02-06 14:33:03 发布