一、缓存概述
缓存是一项在计算机中广泛用来提高性能的技术,它将访问频率高或构造成本高的数据保留在内存中,在缓存有效期内对该数据的访问可以直接从内存中读取,而不必重复执行,这样既节省了系统资源,又
加快了程序运行速度。对于Web Form来说,缓存技术极其重要,因为通过Internet传输数据的速度可能非常缓慢。通过缓存数据,Web Form可以极大地提高应用程序的响应速度和性能,从而改善传输性能。
在Web Form中,缓存用于保留在HTTP请求时的页面或数据,并在无需重新创建的情况下重新使用它们。.NET中有三种可由Web Form使用的缓存:
·页面输出缓存 它缓存请求的整个页面。
·页面片断缓存 它缓存请求页面中的自定义用户控件。
·页面数据缓存 它以编程方式缓存页面中的对象或数据。
我们应该如何区别使用这三种缓存呢?下文我们将以实例来分别讨论它们的使用方法。
二、缓存的使用
1、页面输出缓存的使用
顾名思义,页面输出缓存就是指将整个页缓存起来,以后对该页的请求将由缓存输出,而不必执行创建该页的代码。页面输出缓存是ASP.NET中最常用的一种缓存技术。在访问量很大的站点中,对访问频率高的页即使一次仅缓存一分钟,也会带来巨大的吞吐量收益。
启动页面输出缓存功能很简单,只需使用页面编译指令OutputCache即可:
<% @ OutputCache Duration=”Time” VaryByParam=”none” %>
该指令中,OutputCache指示将页放入缓存,参数Duration设置以秒为单位的缓存有效期,VaryByParam设置缓存随查询字符串中的名称/值对值变化的请求。具体应用请看例1,VaryByParam的应用请看例2。
例1:PageCache.aspx
<%@ OutputCache Duration="30" VaryByParam="none" %>
<%@ Page Language="VB" %>
<HTML>
<HEAD>
<title>页面输出缓存技术</title>
<script runat="server">
Sub Page_Load(obj as object,e as eventargs)
‘输出页面生成时间
lblMessage.text = "欢迎您!现在时间是:" & datetime.now.tostring()
End Sub
</script>
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<div align="center">
<font style="font-size:12pt;font-weight:bolder;color:red">页面输出缓存技术</font>
<hr size=1>
<asp:Label id="lblMessage" runat="server" />
</div>
</form>
</body>
</HTML>
这是一个简单的使用页面输出缓存的例子,它的运行结果是在屏幕上显示页的生成时间。编译指令OutputCache指示页被缓存30秒,VaryByParam的值为none表示该页不随任何 GET 或 POST 参数改变。在缓存有效期内对该页的请求由缓存服务,30 秒后从缓存中移除该页,将显式处理下一个请求并再次缓存页。就本例而言,在缓存30秒内无论如何刷新,当前时间都不会改变,这充分说明在缓存期内页并没有真正执行,它只是直接将缓存页读取出来以响应客户端的请求。只有当缓存过期后再次请求时页才会重新被执行并生成新的当前时间。
那么在页面输出缓存中,参数VaryByParam应该如何使用呢?在开发ASP.NET应用程序时,我们经常要进行参数传递,并用此参数进行条件查询。参数一般可以通过文件名来传递,如PageParamCache.aspx?name=jack 表示传递了值为“jack”的参数name。那么,如何根据参数值来对整个页进行缓存呢?这里就要用到参数VaryByParam了。ASP.NET会根据传递的参数值来判断被请求页是否在缓存中,如果参数值不同,ASP.NET会注册一个缓存遗漏(Cache Miss),即没有在缓存中找到页,然后重新执行该页,并将其存储到缓存中。如果参数相同,ASP.NET将直接从缓存中读取输出。这就意味着同一个页可以根据不同的参数/值生成不同的缓存页。具体应用我们来看例2。
例2:PageParamCache.aspx
<%@ OutputCache Duration="60" VaryByParam="name" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<script language="VB" runat="server">
Sub Page_Load(Src As Object, E As EventArgs)
Dim MyConnection As SqlConnection
Dim MyCommand As SqlDataAdapter
Dim ds As DataSet
Dim strName As String
Dim strSQL As String
queryState = Request.QueryString("name")
If queryState = Nothing
‘未用参数查询
strSQL = "select * from articles"
Else
‘带参数查询
strSQL = "select * from articles where name = '" & strNmae & "'"
End If
MyConnection = New SqlConnection("server=Localhost;uid=sa;pwd=;database=article")
MyCommand = New SqlDataAdapter(strSQL, MyConnection)
ds = New DataSet()
MyCommand.Fill(ds, "articles")
dbgridContent.DataSource=new DataView(ds.Tables(“articles”))
dbgridContent.DataBind()
lblMessage.Text = DateTime.Now.ToString()
End Sub
</script>
<body>
<div align=”center”>
<font style=”font-size:12pt;font-weight:bolder;color:red”>使用带参数的页面输出缓存</font>
<br>按作者分类:<br>
<table cellspacing="0" cellpadding="3" width=700>
<tr>
<td><a href="PageParamCache.aspx?name=张三">张三</a></td>
<td><a href="PageParamCache.aspx?name=李四">李四</a></td>
<td><a href="PageParamCache.aspx?name=王五">王五</a></td>
<td><a href="PageParamCache.aspx?name=钱六">钱六</a></td>
<td><a href="PageParamCache.aspx?name=刘七">刘七</a></td>
</tr>
</table>
<ASP:DataGrid id="dbgridContent" runat="server" /><br>
<i>上次生成时间:</i> <asp:label id="lblMessage" runat="server"/>
</div>
</body>
</html>
例2中,我们指定参数VaryByParam的值是name,作用是指示页根据name值进行缓存处理。当您第一次单击给定姓名的链接时,页面底部会生成一个新的时间戳。此后,只要在一分钟内重新提交对该姓名的请求时,都会得到原来的时间戳,表示该请求已被缓存。当您单击其他姓名的链接时,系统首先会查找以该姓名值存储的缓存页,如果未找到,则重新执行页,并进行缓存,如果找到,则直接输出缓存页。在例2所给的情况下,PageParamCache.aspx最多可以有5个不同的页面输出缓存以响应5个带有不同参数值的链接请求。
如果传递的参数不止一个,那么即使字符串参数与值都相同,但排列次序不同,那么在请求页面时,也将生成不同的缓存页。例如PageParamCache?first=chris&last=payne 和PageParamCache? last=payne&first=chris虽然参数完全相同,但由于排列次序不同,将生成两个不同的缓存页。具体应用大家可以自己试验一下,本文不再赘述。
2、页面片断缓存的使用
有时缓存整页是不可行的——可能必须针对每个请求来创建或自定义该页的各部分。在这种情况下,标识那些构造成本高并且适于缓存的对象或数据通常是值得的。标识了这些项后,可创建它们一次然后将它们缓存一段时间。此外,片断缓存还可用于缓存页输出的各区域。在ASP. NET中,页面片断缓存一般用来存放自定义用户控件。
例如,某一页面既要从数据库中读取最近10条新闻绑定到DataGrid,同时还要将页面访问量计入数据库。很显然,读取新闻绑定DataGrid的操作应该放入缓存,因为新闻更新不会很频繁,这样可以避免频繁地存取数据库。但统计页面访问量操作却不能放入缓存,否则在缓存有效期内的页面请求将被忽略。解决这一对“矛盾”最好的办法就是使用页面片断缓存。在例3中,我们把SQL 查询和绑定DataGrid的操作定义为用户控件(LastedNews.ascx),并将它放入缓存,而父页面(ControlParentcache.aspx)不进行缓存处理以得到真实的访问量统计数据。
例3:父页面ControlParentCache.aspx
<%@ register TagPreFix="zhao" TagName="LastedNews" src="LastedNews.ascx" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>页面片断缓存</title>
<script runat="server">
'统计页面访问次数并存入数据库visitor表
Sub Page_Load(obj as object,e as eventargs)
Dim myConnection As New SqlConnection("server=LocalHost;uid=sa;pwd=;database=news")
myConnection.Open()
dim myCommand as new sqlCommand
Dim myDataReader As SqlDataReader = myCommand.ExecuteReader()
Dim intVisit As Integer = myDataReader.Item("visittimes") + 1
myConnection.Close()
myCommand = New SqlCommand("update visitor set visittimes=" & intVisit , myConnection)
myCommand = Nothing:myDataReader = Nothing:myConnection = Nothing
End Sub
</script>
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<div align="center">
<font style="FONT-WEIGHT:bolder;FONT-SIZE:12pt;COLOR:red">最近十条新闻</font>
<hr size="1">
<zhao:LastedNews runat="server" id="defaultNewsStock" />
</div>
</form>
</body>
</HTML>
例3:用户控件LastNews.ascx
<%@ OutputCache Duration="60" VaryByParam="*" %>
<script runat="server">
‘生成DataGrid
Sub Page_Load(obj as object ,e as EventArgs)
‘将DataView记录集绑定到DataGrid
dbgridLastedNews.DataSource = LoadDataView()
dbgridLastedNews.DataBind()
End Sub
Function LoadDataView() As DataView
‘定义连接并打开连接
Dim myConnection As New SqlConnection("server=LocalHost;uid=sa;pwd=;database=news")
myConnection.open()
‘执行查询操作,并将结果记录集填充到DataView中
Dim myCommand As SqlClient.SqlDataAdapter = New SqlClient.SqlDataAdapter("select top 10 * from news order by id desc", myConnection)
myCommand.Fill(ds, "news")
LoadDataView = New DataView(ds.Tables("news"))
myCommand.Dispose()
End Function
</script>
<asp:DataGrid id="dbgridLastedNews" runat="server"></asp:DataGrid>
在例3中,由于我们把统计访问量的代码放在父页的页面加载函数Page_Load()里,所以我们不能对父页进行缓存处理。在用户控件的定义文件LastedNews.ascx中,我们则清楚地看到对它进行了缓存处理,有效期为60秒。这样无论何时加载父页,Page_Load()函数总是能够得以执行,而绑定DataGrid的用户控件在缓存有效期内可以直接从缓存读出。利用页面片断缓存,我们在保持了父页的动态属性的同时,又可以对用户控件进行缓存处理,可谓实现了“双赢”。
3、页面数据缓存的使用
通俗地说,页面数据缓存就是将对象加入到缓存中。我们可以回想一下创建DataGrid对象的过程:建立sqlConnectionà打开sqlConnectionà创建sqlDataAdapterà带SQL参数运行sqlDataAdapterà填充DataSetà绑定到DataGrid,细数一下创建一个DataGrid至少需要六个步骤,这不仅降低了程序执行速度,也消耗了无须消耗的系统资源。如果在完成这些操作后,将结果(如DataSet)缓存起来有多好!事实上,页面数据缓存最常见的用途就是缓存数据库返回的结果,使它在缓存期内输出时能够很方便快捷地绑定到DataGrid。
我们先来看看例4。例4是一个使用cache类来缓存从数据库返回的数据的例子。
例4:ObjectCache.aspx
<%@page Language=”VB” %>
<@Import Namespace=”System.Data” %>
<@Import Namespace=”System.Data.sqlclient” %>
<script runat=”Server”>
Sub Page_Load(obj as object,e as EventArgs)
If ont Page.IsPostBack then
CreateData()
End If
End Sub
Sub CreateData
Dim source as DataView
把缓存中的DataView赋值给source
Source = cache(“DataView”)
如果缓存是空的(即第一次执行或缓存过期)
If source is nothing then
‘创建并打开连接
Dim myConnection As SqlConnection
myConnection = new sqlConnection("server=LocalHost;uid=sa;pwd=;database=news")
myConnection.open()
dim myCommand as sqlDataAdapter
myCommand = New SqlClient.SqlDataAdapter("select * from news order by id desc", myConnection)
myCommand.Fill(ds, "news")
source= New DataView(ds.Tables("news"))
cache(“DataView”) = source
‘给出DataGrid生成方式信息
lblMessage.text = “显式创建的数据集”
else
lblMessage.text = “从缓存中检索到的数据集”
End If
dbgridNews.DataSource = source
dbgridNews.DataBind()
End Sub
<html><body>
<form runat=”Server” id=”form1”>
<asp:DataGrid id=”dbgridNews” runat=”Server” />
<hr size=1>
<asp:Label id=”lblMessage” runat=”server” />
</form>
</body></html>
当例4被首次加载时,页面将从数据库中取回数据填充到DataSet中,将其绑定到DataGrid,同时将它存储到缓存中。以后数据缓存期内的页面请求,就可以从缓存中得到数据,执行速度自然会大大提高。
与前面两种缓存技术相比,页面数据缓存具有三大优点:首先,它可以缓存任何对象,您无需缓存整个页或用户控件,而可以更细致地进行缓存。其次,Cache类允许您使用动态的失效期(sliding expiration)。最后,可以为放置在Cache类中的对象设置依存关系(dependencies)。这意味着您可以将被缓存的数据设置为依赖于另一项内容。缓存的DataGrid可以依赖于底层的数据。当源数据发生变化时,缓存的DataGrid将被作废,并重新创建一个新的DataGrid。
三、如何高效地使用缓存技术
缓存技术可以极大地提高Web Form的性能。在负载很大的情况下,即使缓存时间非常短,其效果也十分明显。通常,有很多信息可以进行缓存,包括从数据库或文件中取回的数据、复杂计算机的结果以及应用程序的设置等。
虽然本文论述了使用缓存所带来的若干好处,但并不是任何时候任何情况都适合使用缓存,如必须提供最新股票报价的页面。除非设置足够的依存关系,否则在这种情况下使用缓存将是非常糟糕的,因为如果设置了缓存,那么访问者在页面缓存的时间里将无法及时得到最新的股票数据。
下面的表格中列出了常见的应该和不应该使用缓存的情况。
应 该 不 应 该
应该缓存那些经常被访问、同时变化频率不大的数据
应该缓存整个应用程序都要使用的设置或对象,但这些设置和对象必须在其生存期内不会变化不要缓存个人信息。如果缓存个人信息,其他人很容易取得这些信息。
不要缓存包含基于时间值的页面,否则浏览者将无法理解为何时间总是滞后。
不要缓存用户随时都会修改的对象,如购物车。
最后需要强调说明的是,在Web Form调试期间不能使用缓存,否则你对页面所做的修改在缓存过期之前不会得到显式加载。正确的做法应该是在调试结束之后,给需要放入缓存的页面、用户控件或对象加上缓存指令。最后建立部署和安装项目,生成安装数据包,这时候就可以到服务器上去发布你的产品了。