[置顶] 细说Cache
什么是缓存?
Web 应用程序通常都是被多个用户访问。一个Web站点可能存在一个“重量级”的加载,它能够使得站点在访问的时候,拖慢整个服务器。当站点被大量用户同时访问的时候,访问速度缓慢是大部分网站共同存在的问题。为了解决这个问题,我们可以使用一个更高级别的硬件配置,负载均衡器,高带宽,但是加载并不是拖慢站点唯一的“罪魁祸首”,所以我们需要提供一种方案,它也同样能够加速数据访问以及提升性能。而采用缓存正是一种很不错的解决方案。
缓存是一种能够存储我们通常需要使用的数据的技术,它能够把web 页面暂时存储在本地的硬盘上以供后续的检索。这种技术在多个用户同时访问一个站点,或者一个用户多次访问一个站点时,有效地提升了访问速度。为Web应用程序做的缓存可以发生在客户端(浏览器缓存),可以作用在客户端和服务端之间的一个服务器上(代理缓存/反向代理缓存),或者只作用在web服务器本身(页面缓存或数据缓存)。
我们可以选择花费大量的时间来存储缓存数据以提升应用程序的性能,但这并没有真正意义上达到我们的目的。因为如果我们考虑到Web 服务器的负载,我们就不得不考虑缓存数据存储的位置。接下来一节我们来讨论缓存的不同位置。
缓存所在的不同位置
一个web应用程序的缓存要么处于客户端(客户端浏览器),在客户端与服务器之间(代理或反向代理缓存),要么处于服务端(数据缓存、页面输出缓存)。所以我们就能够区分出缓存的位置:
1、 客户端缓存
2、 代理缓存
3、 反向代理缓存
4、 Web服务器端缓存
1、 客户端缓存
在采用客户端缓存时,客户端浏览器通过在本地硬盘上存储缓存的数据作为一个零时文件,或者存储在浏览器的内部存储器上来执行缓存操作。它提供了一种快速访问相同数据的方式,因为它拒绝任何的网络加载以及服务端加载。该数据不能够被其他客户端共享,所以是客户端独有的。
优势
1、 因为数据存储在本地客户端,所以可以很容易地访问
2、 避免了网络传输
劣势
1、 缓存的数据独立于浏览器,所以是无法被共享的
2、 代理缓存
客户端缓存的主要的劣势是数据存储在客户端浏览器,属于客户端私有的。而代理缓存使用一种独有的服务器在服务端与客户端之间以一种共享的“位置”来缓存数据,所以所有的客户端都可以使用相同的共享数据。代理服务器(例如微软的代理服务器)来满足所有对web页面的请求,而无需将请求跨越网络传输到最终的web服务器,这能够使得快速访问成为现实。
![](http://hi.csdn.net/attachment/201112/21/0_1324471505WM0R.gif)
代理缓存通常位于网络的网关附近来减少带宽的使用。有时通过多代理缓存服务器的使用来缓解大量用户访问代理的压力。这被称之为一种缓存集群。
![](http://hi.csdn.net/attachment/201112/21/0_1324471542dEZe.gif)
优势
1、 数据被缓存在代理服务器可以很容易就被访问
2、 减少网络通信
劣势
1、 涉及到部署以及为了维护代理缓存服务器的基础设施的开销
3、反向代理缓存
有些代理缓存服务器可以被防止到web 服务器的前端来减少他们接受到的请求的数量。它允许代理服务器处理通常接受到的请求,而仅仅传递其他的“特殊”请求给服务器。这称之为反向代理。
![](http://hi.csdn.net/attachment/201112/21/0_1324472077Ttte.gif)
优势
1、 被缓存在反向代理服务器上的数据可以很容易地获得
2、 减少请求的数目
劣势
1、 当该服务器被配置在web服务器的前端,它可能导致额外的网络通信
4、web服务器缓存
在web服务端做缓存,缓存的数据被存储在web服务器上。数据缓存以及页面缓存可以使用web服务器这种缓存方案。
![](http://hi.csdn.net/attachment/201112/21/0_1324472179Uyzd.gif)
优势
1、 提高站点的性能,减少了从数据库以及其他服务器检索数据的开销。
劣势
1、 增加了网络加载开销
缓存的优势
1、 减少服务端的负载
2、 减少了带宽的消耗
ASP.NET中的缓存
Asp.net提供对页面、部分页面(页面片段),以及数据的缓存。缓存一个动态生成的页面被称之为页面输出缓存。当一个动态生成的页面被缓存,它只是第一次被访问。任何后续对相同页面的请求都将从缓存返回。Asp.net也提供缓存页面部分的缓存方式,被称之为部分页面缓存或者页面片段缓存。当采用数据缓存时被缓存的服务端数据(例如来自数据库的数据、XML数据)能够被简单地访问,而无需做再次检索。缓存减少了从数据库或其他数据源获取数据的开销。Asp.net提供一种“全套的”数据缓存引擎,包括清除(基于缓存的优先级),过期,文件,键,以及时间依赖。在asp.net中,有两种缓存形式可以提升性能。
![](http://hi.csdn.net/attachment/201112/21/0_1324472225k2L2.gif)
在上面的图片中,(1)用来返回页面的缓存,这意味着它是用于输出缓存的,而(2)使用数据缓存,通过存储数据来减少获取数据的开销。
Asp.net支持两种形式的过期策略,这决定了什么时候对象将失效或者被从缓存中移除。
绝对过期:绝对过期发生在一个标识的时间。绝对过期时间被标识为一种全日期格式(hh:mm:ss)。在标识的时间,对象将从缓存中过期。
Asp.net 支持三种类型的缓存:
1、 页面输出缓存【输出缓存】
2、 页面片段缓存【输出缓存】
3、 数据缓存
不同类型的缓存
1、 页面输出缓存:在开始页面输出缓存之前,我们需要知道生成一个页面的过程,因为基于生成的页面,我们应该能够理解为什么我们应该使用缓存。一个aspx页面经过两个阶段的处理后被完成。首先,代码被编译进MSIL。然后,在执行期间,MSIL被编译到本地代码(通过JIT,即‘即时编译器’)。当我们编译站点的时候,一个asp.net页面的整个代码都被编译如MSIL,但是在执行的时候,仅仅只有部分MSIL被转换为本地代码,这提升了性能。
![](http://hi.csdn.net/attachment/201112/21/0_13244722983FS9.gif)
现在,不管我们得到了什么,如果有某个页面不是经常的变化,JIT就不需要每次都编译它。我们可以为那些内容相对静态的页面使用输出缓存。而不是对每个用户的请求,都去生成一个页面,我们可以使用页面输出缓存,以使得它可以从缓存中访问其自身。页面只需要被生成一次,然后的请求都从缓存中获取。页面输出缓存允许一个页面的整个内容都存储在缓存中。
![](http://hi.csdn.net/attachment/201112/21/0_1324472504sOO0.gif)
在这幅图中,当第一次请求生成完页面,页面被缓存,而对之后的相同页面的请求,该页面会被从缓存中检索出,而不是再次生成。
对输出缓存,一个OutputCache属性可以被直接添加到任何一个asp.net页面,指定一个该页面被缓存的持续时间(秒)
示例
我们也可以在后台代码里设置缓存属性
我们不得不提及duration以及VaryByParam属性。Duration定义缓存将会持续多长时间。VaryByParam定义不同的缓存参数值
![](http://hi.csdn.net/attachment/201112/21/0_1324472760ZtOT.gif)
就像上面展示的这幅图一样,如果我们对一个页面使用一个查询字符串,我们需要基于该参数缓存所有的页面,我们可以使用VaryByParam属性。基于查询字符串,数据应该被缓存,当用户请求一个页面,并携带一个查询字符串(图片中是ID),页面也能够在缓存中被检索到。下面的例子描述了VaryByParam属性的使用。
示例:
下面这幅图展示了最通用也是最重要的输出缓存的属性:
我们制定的所有outputCahce的属性,都来自System.Web.HttpCachePolicy类的实例。由asp.net提供的缓存策略的完整实现都封装在HttpCachePolicy类中。
输出缓存位置
就像我刚才提到的,我们可以存储缓存数据在不同的地方——客户端,服务器,代理服务器。现在,我将怎么设置缓存数据的位置。如果我们存储缓存数据,它将从缓存中检索出页面从而节省了页面的生成时间。但是另一种方式是可以保存数据在客户端浏览器,它可以减少网络通信。而OutputCache执行能够使用所有的三种方式的缓存——服务器,客户端,代理(默认)。
接下来的飙歌展示了缓存位置的明细。它展示了缓存处在的位置,以及对Cache-Control和过期时间头的影响。
![](http://hi.csdn.net/attachment/201112/21/0_1324473140GMNA.gif)
例如,如果你为Location属性设置Client值,页面将不会被保存在服务端的缓存中,但是响应中将包含一个Cache-Control响应头(通过使用Cache-Control头,页面可以指明是否他们应该被缓存在一个代理上)设值为private,并且一个Expires头(Http 响应,指明该页面需要重新从服务器请求的日期和时间)值为一个时间段,它是由Duration属性指明的。
例子
该自理将保存缓存120秒,缓存的数据不会保存在服务端,而应该在客户端浏览器。
2、页面片段缓存:asp.net提供一种对页面片段的缓存方案,称之为页面片段缓存。为了缓存一个页面的一部分,你必须首先将这“部分页面”封装到一个用户控件中。在用户控件的源文件中,加入一个outputcache指令,并指明Duration以及VaryByParam属性。当用户控件在运行时被家载入一个页面中,它将会被缓存,并且所有的引用了相同用户控件的其他页面也将能够从缓存中检索数据。
![](http://hi.csdn.net/attachment/201112/21/0_1324473254CqLD.gif)
接下来的示例显示了缓存片段的一些细节:
示例
这里我已经缓存了一个用户控件,所以无论何时我们再一个页面中使用它,该页面的这部分都将会缓存起来。
3、数据缓存:数据缓存能够动态地提供一个应用程序的性能,因为它减少了数据检索所带来的一切“开销”。其实很简单,数据缓存将请求的数据存储在缓存中,这样web服务器就不需要对到来的所有请求去请求数据库服务器,它能够增加web站点的性能。为了实现数据缓存,我们需要找到那些可访问的并且非常普遍的数据。并且数据缓存是也是一种“全套特性”的缓存引擎,能够使你在多个Http请求以及多个来自相同应用程序里的会话之间检索和存储数据。
![](http://hi.csdn.net/attachment/201112/21/0_1324473347ilL8.gif)
上面的图片展示了数据如何从数据库中北之间访问以及数据是如何从缓存中被重新检索。数据缓存不仅可以缓存SQL Server中的数据,我们甚至能够存储如图1.4中所示的其他数据源。
现在,让我们看看如何在web应用程序中实现数据缓存。这里有三种不同的方式来将数据或者对象加入缓存。但是,基于不同的方案,我们有不同的访问数据的方式。这些方法是Cache[],Cache.add(),cache.insert()。接下来的表格向你清晰地展示了这三种方法的异同点:
![](http://hi.csdn.net/attachment/201112/21/0_1324473401NmlL.gif)
Cache[]是一种非常容易使用的属性,但cache.insert()以及cache.add()给我们对于缓存数据更多的控制。
现在我们需要看看cache.insert()以及Cache.Add()的细节。Cache.Insert()有四个重载方法,而Cache.Add()怎没有重载方法。接下来的表格展示了这些方法的大部分通用属性。
![](http://hi.csdn.net/attachment/201112/21/0_1324473447Ct8W.gif)
开始的两个是强制被Cache.Insert()方法使用的,而其他几个则有所不同。
缓存依赖
使用缓存依赖,我们可以为某些数据或可能改变的实体设置依赖。所以我们能够通过设置缓存依赖来更新或者移除缓存。在asp.net中支持三种类型的依赖:
(1) 基于文件的依赖
(2) 基于键的依赖
(3) 基于时间的依赖
基于文件的依赖:基于文件的依赖,在当磁盘上的文件改变时,可以让一个通常的缓存项失效。
使用缓存依赖,当文件改变时,我们可以从缓存中强制失效某些缓存项。我们可以设置多个文件依赖。在这样的情况下,依赖应该被建立在一组文件或文件夹上。
使用:基于文件的依赖是非常有用的,当你需要更新更新的数据是基于某个文件的时候。例如,一个新闻站点总是从一个文件中去获取数据,但是,如果某些爆炸性的新闻出来,他们只需要更新下文件,然后缓存就该失效,并且在失效时间之内,我们通过OnRemoveCallBack回调可以重新加载缓存中的已被更新的数据。
基于键的缓存依赖:基于键的依赖,在当另一个缓存项改变时,让一个通常的缓存项失效。
使用:当我们有多个内部关联对象在缓存中时,这将会非常有用,我们需要更新或过期所有的这些对象。
基于时间的缓存依赖:基于时间的依赖会让一个缓存项在预定的时间失效。Cache的Cache.Insert()方法被用来创建一个基于时间的缓存依赖。可以对其设置两种类型的时间:
(1) 绝对时间
(2) “滑动”时间(相对的)
绝对:给一个缓存项设置一个失效的绝对时间。它是一个全时间格式,包含(hh:mm:ss)。
滑动:对每个请求重置缓存项的失效时间。当缓存项需要为了来自多个客户端的请求都保持存活的时候,它是非常有用的。
对这些所有的依赖,asp.net允许下面的操作发生:
自动失效:那些在使用中并且没有任何依赖的缓存项都会自动失效。
支持回调:缓存对象能够被配置来调用一个被“赋予”的一段代码,当一个缓存项被从缓存中移除时,这些代码就会被调用。这给了你机会去更新缓存。我们可以使用OnRemoveCallback()。
缓存使用的注意事项
输出缓存注意事项:
1、 使得一个页面上的输出缓存在通常情况下能够访问,对所有这些访问都确保他们返回一致的内容。
2、 当对一个页面使用输出缓存时,确保不引入不正确的行为,不为特定的用户做处理。
3、 谨慎地决定缓存页面的过期时间,平衡访问速度与内存消耗以及高速缓存一致性的正确访问(吞吐量)。
4、 如果你使用的是VaryByParam=’*’,考虑使用页面滑动过期。
数据缓存注意事项:
1、 数据缓存不是一个共享的可更新状态的容器
2、 缓存的数据是通常需要访问的以及获取相对比较“昂贵”的。
3、 如果数据依赖于文件,文件夹,或者其他的缓存实体,使用一种CacheDependency来确保它满足当前的需求。
缓存类型的建议
场景一:生成的页面通常都相同,但是有某些展示的表单通常会更新(使用页面片段缓存)
场景二:生成的页面总是在变化,但是有一些对象并不是经常变动(使用数据缓存)
场景三:生成的页面每隔几个小时变化一次,当信息通过一种自动处理过程从数据库中被加载时(使用页面输出缓存并且设置过期时间来匹配数据库的更新时间)。
更多关于缓存使用的注意事项,请参考:开发中常见的十种对缓存的错误使用
----部分内容参考自CodeProject,特此注明!