当你的web站点采用asp技术建立的初期,可能感觉到的是asp动态网页技术带来的便利性,以及随意修改性、自如的http控制。但是,随着访问量的增加,你一定会发现自己的站点访问速度越来越慢,IIS重新启动得越来越频繁。接下来,你一定想优化asp,诸如更换性能更优异的数据库、建立索引、编写存储过程等等。这些措施有些不需要增加成本压力,有些则成本压力很大(譬如丛access到SQL),而且效果还不一定。
2、需要更频繁的打开/关闭数据库连结(如果使用数据库技术—通常asp都会采用数据库作为数据存储),严重消耗资源的事物数量、以及事务相互竞争资源引起的死锁、会增加网络I/O或者CPU消耗。
3、如果采用session的话,IIS为了维持状态,会消耗更多内存,而内存消耗可能会引发物理内存不够,引起物理内存同辅存间的频繁交换,从而引起代码执行的停顿,web响应阻滞。
所谓文件缓存,就是根据逻辑判断,一段时间内某个asp的特定执行将不会有很大的变动,因而将内容以静态html的形式存放,然后以web的重定向技术让客户端访问静态文件,以达到减少CPU、数据库资源等的需求。这样的应用很多,譬如很多论坛就是在回复贴子的时候将整个贴子重新生成一个静态文件,然后进行重定向的,例如 donews.com的论坛。该成静态还有一个副作用(好处)--可以很容易被google等搜索引擎收录。一些所谓新闻发布系统的都采用了此技术。
所谓文件缓存,也是基于逻辑判断,某部分数据(通常是需要消耗资源的大容量数据库查询取得)在一定时间内不会改变,所以我们可以将这些数据利用文件的形式进行存储,当需要时候,可以通过读取文件来获取数据,避免增加数据库的负担。例如,我们通常将一些数据以xml格式存储,然后利用xslt技术实现显示(xml处理通常需要大量CPU资源,所以通常是IE直接读取xml到客户端在客户的CPU上进行处理)。CSDN的论坛就是这样处理的。
除此之外,还可以考虑在内存中处理缓存,将需要及时响应的内容存储在内存中,一旦访问需求,立即从快速的贮存中输送出去。如果极大量的访问需求集中在几个少量的页面或者主存足够多,我想采用主存缓存一定可以大幅度提高web访问性能。
分析自己的站点,这样的页面很多。譬如一个站点通常都有新闻资讯类的栏目,这些栏目通常都是站点维护人员在一天的某个时间发布资料,之后很少改动页面。那么这些页面就适合于采用静态文件缓存。实际上,所谓新闻发布系统就是这么做的,那么那也可以参考这些系统的思想改造自己的原有动态asp页面。
除了新闻资讯之类的栏目所有访问者都看一个界面外,论坛等消耗资源的应用一般也可以设计成统一逻辑生成(同一个贴子,张三李四看的都一样),针对这类应用页面我们也可以采用静态缓存来实现。也可以考虑将数据片断化,利用脚本技术在服务器处理能力之外也就是客户端浏览器进行处理。
动态查询页面,每个人的查询内容不一样,所以显示结果不大一样,所以不大可能将查询结果生成缓存,所以采用缓存较为复杂且缓存利用率底下,造成管理成本上什(假设你缓存了1000个查询关键字,那么管理这些关键字同缓存的对应也是麻烦事)。
那如何造成文件不存在呢?删除。我们在发表一个新的贴子、删除贴子、移动贴子的时候我们可以将类似listThread_xxx_p2.htm之类的静态文件全部删除。这样就通知了何时要进行缓存。
最后,提醒,通过web访问,asp编程环境下,很多人采用xmlHTTP组件访问,这会造成很多问题。xmlhttp自身会cache请求的资源,导致我们通过此组件请求得到的内容不是最新的,造成逻辑上的混乱。所以,应当选择xml Server http对象或者winhttp组件来实现web请求资源。
使用ASP中的缓存技术可以很大程度上提高你的网站性能,其实这些实现方法是非常的简单,它将说明如何在服务器上的缓存是如何工作以及你如何使用一种被称为断开连接的ADO连接技术。
在介绍这些技术之前先说明一下到底什么是ASP的缓存技术。
所谓缓存其实就是在内存中开辟一个用来保存数据的空间,使用缓存你就不用频繁的访问你保存在硬盘上的数据了,灵活的使用缓存你就免去了心疼的看着可怜的硬盘饱受读数据时的折磨了。当你一旦执行了一个查询动作,并且将查询结果放入缓存中后,你就可以很迅速的重复访问这些数据了。而如果你不把数据放入缓存的话,当你再次执行这个查询时,服务器会将进程耗费在从数据库中获取并排序上了。
当数据保存在缓存中时,再次查询时耗费的时间主要是在显示数据的时间上了。
也就是说,我们不应该把经常需要改变的数据放到服务端的缓存中,我们应该把改变少,但是又需要经常访问的数据放到缓存中。
现在我们先讨论ASP在服务端使用缓存的技术,过会再讨论ASP如何在客户端使用
缓存的技术。
当你有大量的数据(静态的,就是说变动比较少的)需要显示给客户端时,你就可以考虑使用服务端的缓存技术了。这种技术尤其适用于那些显示风格一致性比较强的网站(呵呵,对于非主流的网站可不好用的说。)
其实实现方法特别的简单,大家只要看看下面这个简单的例子就明白了。
这是一个用来显示书籍分类的例子程序
DisplayBooks.ASP文件:
<%@LANGUAGE=JavaScript%>
<html>
<body>
<formmethod=post>
书籍分类;<%=getBooksListBox()%>
<p>
<inputtype=submit>
<%
functiongetBooksListBox()
{
BooksListBox=Application("BooksListBox")
if(BooksListBox!=null)returnBooksListBox;
crlf=String.fromCharCode(13,10)
BooksListBox="<selectname=Books>"+crlf;
SQL="Select*FROMBooksorDERBYName";
cnnBooks=Server.CreateObject("ADODB.Connection");
cnnBooks.Open("Books","Admin","");
rstBooks=cnnBooks.Execute(SQL);
fldBookName=rstBooks("BookName");
while(!rstBooks.EOF){
BooksListBox=BooksListBox+"<option>"+
fldBookName+""+crlf;
rstBooks.MoveNext();
}
BooksListBox=BooksListBox+""
Application("BooksListBox")=BooksListBox
returnBooksListBox;
}
%>
很简单把,其实就是用了很简单的Application技术,而且就一句话的不同:
Application("BooksListBox")=BooksListBox
你可以验证一下你就会发现服务器上的请求数量会降低不少的。这种情况尤其适合与那些更新不是很频繁的网站内容,例如你一天(或则很长时间)只更新一次。
下面再讨论一种客户端的缓存技术这种技术也叫断开连接的ADO连接技术(翻译水平太次,听上去怎么这么别扭)。这种技术主要使用在用来保存用户个人信息,例如用户的密码,代号等等上面。它主要使用了ADO的一些属性。同时也回答了一些网友曾经提到过的能否在Applocation中使用ADO对象的问题。解释不清楚,下面让代码来发言:
文件GLOBAL.ASA:
<!--METADATATYPE="TypeLib"FILE="C:\ProgramFiles\Common
Files\system\ado\msado15.dll"-->
<SCRIPTLANGUAGE=VBScriptRUNAT="Server">
SubApplication_OnStart
SQL="SelectUserName,PasswordFROMUserInfo"
cnnUsers="DSN=User"
SetrsUsers=Server.CreateObject("ADODB.Recordset")
'注意下面这两句话,就是用来实现那个叫可用的断开连接的ADO技术
rsCustomers.CursorLocation=adUseClient
rsCustomers.OpenSQL,cnnAdvWorks,adOpenStatic,AdLockReadOnly
'断开RecordSet的和数据库的连接
rsCustomers.ActiveConnection=Nothing
SetApplication("rsCustomers")=rsCustomers
EndSub
文件Users.ASP
<%
'Clone方法使得每个用户拥有自己的一个RecordSet集合
SetyourUsers=Application("rsUsers").Clone
SetUserName=yourUsers("UserName")
SetPassword=yourUsers("Password")
DoUntilyourUsers.EOF
%>
用户姓名:<%=UserName%>用户密码:<%=Password%>
<%
yourUsers.MoveNext
Loop
%>
至于缓存的作用,我想我也不用再多说了,它的作用已经很明显,特别是对于信息量非常大或是全数据库页面的网站,他能很好地利用主机的内存资源,加速ASP的执行效率,减轻服务器的负担,而动网在这一方面做得是最突出的,像他现在的dvbbs7.1.0版,更是在缓存的利用上更上一层楼,前后台大多的操作都和缓存有关,而现在动网里用的也就是迷城浪子的缓存类,下面列出动网的三大高手写的ASP缓存类
木鸟写的
'**********************************************
'vbsCache类
'
'属性valid,是否可用,取值前判断
'属性name,cache名,新建对象后赋值
'方法add(值,到期时间),设置cache内容
'属性value,返回cache内容
'属性blempty,是否未设置值
'方法makeEmpty,释放内存,测试用
'方法equal(变量1),判断cache值是否和变量1相同
'方法expires(time),修改过期时间为time
'木鸟2002.12.24
'
http://www.aspsky.net/
'**********************************************
classCache
privateobj'cache内容
privateexpireTime'过期时间
privateexpireTimeName'过期时间application名
privatecacheName'cache内容application名
privatepath'uri
privatesubclass_initialize()
path=request.servervariables("url")
path=left(path,instrRev(path,"/"))
endsub
privatesubclass_terminate()
endsub
publicpropertygetblEmpty
'是否为空
ifisempty(obj)then
blEmpty=true
else
blEmpty=false
endif
endproperty
publicpropertygetvalid
'是否可用(过期)
ifisempty(obj)ornotisDate(expireTime)then
valid=false
elseifCDate(expireTime)<nowthen
valid=false
else
valid=true
endif
endproperty
publicpropertyletname(str)
'设置cache名
cacheName=str&path
obj=application(cacheName)
expireTimeName=str&"expires"&path
expireTime=application(expireTimeName)
endproperty
publicpropertyletexpires(tm)
'重设置过期时间
expireTime=tm
application.lock
application(expireTimeName)=expireTime
application.unlock
endproperty
publicsubadd(var,expire)
'赋值
ifisempty(var)ornotisDate(expire)then
exitsub
endif
obj=var
expireTime=expire
application.lock
application(cacheName)=obj
application(expireTimeName)=expireTime
application.unlock
endsub
publicpropertygetvalue
'取值
ifisempty(obj)ornotisDate(expireTime)then
value=null
elseifCDate(expireTime)<nowthen
value=null
else
value=obj
endif
endproperty
publicsubmakeEmpty()
'释放application
application.lock
application(cacheName)=empty
application(expireTimeName)=empty
application.unlock
obj=empty
expireTime=empty
endsub
publicfunctionequal(var2)
'比较
iftypename(obj)<>typename(var2)then
equal=false
elseiftypename(obj)="Object"then
ifobjisvar2then
equal=true
else
equal=false
endif
elseiftypename(obj)="Variant()"then
ifjoin(obj,"^")=join(var2,"^")then
equal=true
else
equal=false
endif
else
ifobj=var2then
equal=true
else
equal=false
endif
endif
endfunction
endclass
木鸟 类例子vbsCache类
'
'属性valid,是否可用,取值前判断
'属性name,cache名,新建对象后赋值
'方法add(值,到期时间),设置cache内容
'属性value,返回cache内容
'属性blempty,是否未设置值
'方法makeEmpty,释放内存,
'方法DelCahe,删除内存
'方法equal(变量1),判断cache值是否和变量1相同
'方法expires(time),修改过期时间为time
'用法
setmyCache=NewCache
myCache.name="BoardJumpList"'定义缓存名
ifmyCache.validthen '判断是否可用(包括过期,与是否为空值)
response.writemyCache.value '输出
else
................
BoardJumpList=xxx
myCache.addBoardJumpList,dateadd("n",60,now) '写入缓存xxx.add内容,过期时间
response.writeBoardJumpList'输出
endif
myCache.makeEmpty()释放内存
mycache.DelCahe() 删除缓存
迷城浪子写的ClassCls_Cache
Rem==================使用说明====================
Rem=本类模块是动网先锋原创,作者:迷城浪子。如采用本类模块,请不要去掉这个说明。这段注释不会影响执行的速度。
Rem=作用:缓存和缓存管理类
Rem=公有变量:Reloadtime过期时间(单位为分钟)缺省值为14400
Rem=MaxCount缓存对象的最大值,超过则自动删除使用次数少的对象。缺省值为300
Rem=CacheName缓存组的总名称,缺省值为"Dvbbs",如果一个站点中有超过一个缓存组,则需要外部改变这个值。
Rem=属性:Name定义缓存对象名称,只写属性。
Rem=属性:value读取和写入缓存数据。
Rem=函数:ObjIsEmpty()判断当前缓存是否过期。
Rem=方法:DelCahe(MyCaheName)手工删除一个缓存对象,参数是缓存对象的名称。
Rem========================
PublicReloadtime,MaxCount,CacheName
PrivateLocalCacheName,CacheData,DelCount
PrivateSubClass_Initialize()
Reloadtime=14400
CacheName="Dvbbs"
EndSub
PrivateSubSetCache(SetName,NewValue)
Application.Lock
Application(SetName)=NewValue
Application.unLock
EndSub
PrivateSubmakeEmpty(SetName)
Application.Lock
Application(SetName)=Empty
Application.unLock
EndSub
PublicPropertyLetName(ByValvNewValue)
LocalCacheName=LCase(vNewValue)
EndProperty
PublicPropertyLetValue(ByValvNewValue)
IfLocalCacheName<>""Then
CacheData=Application(CacheName&"_"&LocalCacheName)
IfIsArray(CacheData)Then
CacheData(0)=vNewValue
CacheData(1)=Now()
Else
ReDimCacheData(2)
CacheData(0)=vNewValue
CacheData(1)=Now()
EndIf
SetCacheCacheName&"_"&LocalCacheName,CacheData
Else
Err.RaisevbObjectError+1,"DvbbsCacheServer","pleasechangetheCacheName."
EndIf
EndProperty
PublicPropertyGetValue()
IfLocalCacheName<>""Then
CacheData=Application(CacheName&"_"&LocalCacheName)
IfIsArray(CacheData)Then
Value=CacheData(0)
Else
Err.RaisevbObjectError+1,"DvbbsCacheServer","TheCacheDataIsEmpty."
EndIf
Else
Err.RaisevbObjectError+1,"DvbbsCacheServer","pleasechangetheCacheName."
EndIf
EndProperty
PublicFunctionObjIsEmpty()
ObjIsEmpty=True
CacheData=Application(CacheName&"_"&LocalCacheName)
IfNotIsArray(CacheData)ThenExitFunction
IfNotIsDate(CacheData(1))ThenExitFunction
IfDateDiff("s",CDate(CacheData(1)),Now())<60*ReloadtimeThen
ObjIsEmpty=False
EndIf
EndFunction
PublicSubDelCahe(MyCaheName)
makeEmpty(CacheName&"_"&MyCaheName)
EndSub
EndClass
迷城浪子 类例子
SetWydCache=NewCls_Cache
WydCache.Reloadtime=0.5'定义过期时间(以分钟为单会)
WydCache.CacheName="pages" '定义缓存名
IFWydCache.ObjIsEmpty()Then''判断是否可用(包括过期,与是否为空值)
Response.writeWydCache.Value
Else
..................
BoardJumpList=xxx
WydCache.Value=BoardJumpList'写入内容
Response.writeBoardJumpList
Endif
mycache.DelCahe("缓存名") 删除缓存
slightboy写的
'========================
'clsCache.asp
'========================
'==begin:2004-6-2621:51:47
'==copyright:slightboy(C)1998-2004
'==email:
[email protected]
'========================
'========================
'DimApplication(2)
'Application(0)Counter计数器
'Application(1)dateTime放置时间
'Application(2)Content缓存内容
PublicPREFIX
PublicPREFIX_LENGTH
PrivateSubClass_Initialize()
PREFIX="Cached:"
PREFIX_LENGTH=7
EndSub
PrivateSubClass_Terminate
EndSub
'设置变量
PublicPropertyLetCache(ByRefKey,ByRefContent)
DimItem(2)
Item(0)=0
Item(1)=Now()
IF(IsObject(Content))Then
SetItem(2)=Content
Else
Item(2)=Content
EndIF
Application.Unlock
Application(PREFIX&Key)=Item
Application.Lock
EndProperty
'取出变量计数器++
PublicPropertyGetCache(ByRefKey)
DimItem
Item=Application(PREFIX&Key)
IF(IsArray(Item))Then
IF(IsObject(Item))Then
SetCache=Item(2)
Else
Cache=Item(2)
EndIF
Application(PREFIX&Key)(0)=Application(PREFIX&Key)(0)+1
Else
Cache=Empty
EndIF
EndProperty
'检查缓存对象是否存在
PublicPropertyGetExists(ByRefKey)
DimItem
Item=Application(PREFIX&Key)
IF(IsArray(Item))Then
Exists=True
Else
Exists=False
EndIF
EndProperty
'得到计数器数值
PublicPropertyGetCounter(ByRefKey)
DimItem
Item=Application(PREFIX&Key)
IF(IsArray(Item))Then
Counter=Item(0)
EndIF
EndProperty
'设置计数器时间
PublicPropertyLetdateTime(ByRefKey,ByRefSetdateTime)
DimItem
Item=Application(PREFIX&Key)
IF(IsArray(Item))Then
Item(1)=SetdateTime
EndIF
EndProperty
'得到计数器时间
PublicPropertyGetdateTime(ByRefKey)
DimItem
Item=Application(PREFIX&Key)
IF(IsArray(Item))Then
dateTime=Item(1)
EndIF
EndProperty
'重置计数器
PublicSubResetCounter()
DimKey
DimItem
Application.Unlock
ForEachKeyinApplication.Contents
IF(Left(Key,PREFIX_LENGTH)=PREFIX)Then
Item=Application(Key)
Item(0)=0
Application(Key)=Item
EndIF
Next
Application.Lock
EndSub
'删除某以缓存
PublicSubClear(ByRefKey)
Application.Contents.Remove(PREFIX&Key)
EndSub
'清空没有使用的缓存
PublicSubClearUnused()
DimKey,Keys,KeyLength,KeyIndex
ForEachKeyinApplication.Contents
IF(Left(Key,PREFIX_LENGTH)=PREFIX)Then
IF(Application(Key)(0)=0)Then
Keys=Keys&VBNewLine&Key
EndIF
EndIF
Next
Keys=Split(Keys,VBNewLine)
KeyLength=UBound(Keys)
Application.Unlock
ForKeyIndex=1ToKeyLength
Application.Contents.Remove(Keys(KeyIndex))
Next
Application.Lock
EndSub
'清空所有缓存
PublicSubClearAll()
DimKey,Keys,KeyLength,KeyIndex
ForEachKeyinApplication.Contents
IF(Left(Key,PREFIX_LENGTH)=PREFIX)Then
Keys=Keys&VBNewLine&Key
EndIF
Next
Keys=Split(Keys,VBNewLine)
KeyLength=UBound(Keys)
Application.Unlock
ForKeyIndex=1ToKeyLength
Application.Contents.Remove(Keys(KeyIndex))
Next
Application.Lock
EndSub
EndClass
slightboyn类例子
SetWyd=NewJayCache
Wyd.dateTime("Page")=时间
IfWyd.Exists("Page")Then
Response.writeWyd.Cache("Page")'输出
Else
Wyd.Cache("Page")=xxx 写入
Responxe.writexxx
EndIF
Wyd.Clear("page")'删除缓存