ASP 可以快速執行你的動態網頁,但你也可以透過緊縮程式碼和資料庫連線以使它們執行更快。這是一篇關於如何精簡程式碼和Asp 特徵以獲得最快執行速度的詳細文章。對於一個急燥的用戶來說,任何在按下用戶按鈕到結果出現在它們的螢幕之間的延遲可能意味著它們會轉到瀏覽其它的網站?假如你的是商業站點,這有可能意味著失去潛在的銷售。
我們沒有任何辦法控制使用者的頻寬,但我們的確能透過優化Asp 網站來獲得最佳的*能。大部分潛在*能的提升是透過系統改變而不是緊縮代碼,一個不合適的想法是,一旦遇到系統效率問題,就向系統**者提意見要其升級系統。
首先,哪個因素可能影響Asp的*能?很不幸,有很多因素?以下這些只是其中的一部分:
可用頻寬
伺服器上的處理器和其它硬體的速度
在伺服器上運行的其它程式(比如像那些OpenGL螢幕保護程式!)
資料庫連線模式,連線池,資料庫系統本身(例如Oracle優於Sql Server,Sql server優於Access)
所使用的語言
預存程序優於行式Sql語句
使用編譯組件而不是VB或JavaScript,好的Asp程式設計經驗,例如錯誤處理等
一些以上的因素可能已經被有IIS 知識經驗的開發者普遍留意到了,但其它的可能對他們來說是十分複雜的問題。在這篇文章裡, 將試著解釋所有影響Asp*能的每個因素,讓我們來看看那些在我們刮鬍子的幾毫秒內就能做到的主要事情。
ASP腳本大小
你是腳本頁(還有其它頁面)是不是比必須的長度要長?這是一開始執行就會降低Asp *能的東西。 ASP 腳本在用來獲取資訊和格式化輸出的時候是十分有用的,但腳本也是逐行解釋執行,所以你的腳本越長,執行它的時間也就越長。
如果你的腳本很龐大,怎麼做才能減少腳本的長度呢?這裡有幾點建議:
你可以將它們轉換成伺服器端元件,也就是說,做成VB動態連結函式庫DLL或透過先進的Windows程式語言或適當的COM 介面語言將它轉換成未編譯元件?並且在伺服器端註冊它們。有關的快速指南可以在
http://www.webdevelopersjournal.com/articles/activex_for_asp.html找到。對一個寫得好的ActiveX 元件進行編譯不但能大幅提高*能,還可以保護你的軟體(腳本),尤其當你將你的Asp網站發佈在第三方主機上的時候。
因為腳本是逐行解釋執行的,所以剔除多餘的腳本或建立更高效率的腳本能夠改進*能。如果你在單一Asp 檔案中有數百行的程式碼,可能這樣做你能很好地劃分使用者,買賣和資料服務。事實上,如果你這樣做,可能會找出一些冗餘的程式碼:如果你需要輸出幾個表格,你可以寫一個通用函數來輸出一個表格,只是多次呼叫它。
在講述Asp 腳本的大小問題的時候,不得不提及包含檔案的大小。當你使用一個包含文件的時候,整個包含文件被裝入,當包含文件被包含的時候,相當於在Asp 文件本身寫下那部分程式碼。因此,如果你在一個冗長的包含文件裡定義了很多通用的方法和定義,要明白到在你包含該文件的時候,不管你要不要用到裡面的每個方法和定義,它都是被整個裝入的。 ASP 快取全部的展開程式碼,這會降低查找效率在這種情況下,包含檔案必須分割成更小的,模組化的檔案。也要明白包含檔案被伺服器視為單獨的頁面請求,使用太多的包含檔案會影響下載時間。
<!-- #include file=Header.asp -->
<!-- #include file=Footer.asp -->
<SCRIPT language=vbscript runat=server>
Sub Main()
WriteHeader
WriteBody
WriteFooter
End Sub
Sub WriteBody()
……
End Sub
Main?'呼叫過程Main
</SCRIPT>
假如你的腳本冗長的話,請使用Response.IsClientConnected。這意味著在客戶端不再連接到伺服器的時候,你的伺服器CPU能避免循環等待。
<%
'檢查客戶端是否仍在連接
If Not Response.IsClientConnected Then
'仍然連接著,處理程序
Else
'斷開
End If
%>
Interspersing ASP and HTML
每個人都這樣做?當我們輸出表格的時候,我們會在ASP 和HTML程式碼間轉換,而這是一個不好的習慣。例如:
<HTML>
<BODY>
<%
Set MyConn = Server.CreateObject(ADODB.Connection)
MdbFilePath = Server.MapPath(sample.mdb)
MyConn.Open Driver={Microsoft Access Driver (*.mdb)}; DBQ= & MdbFilePath & ;
SQL_query = SELECT * FROM Friends
Set RS = MyConn.Execute(SQL_query)
WHILE NOT RS.EOF
%>
<LI><%=RS(Name)%>: <A HREF=>Homepage</A>
<%
RS.MoveNext
WEND
%>
</BODY>
</HTML>
另一個普遍的例子是使用IF語句的時候:
<%
If Not Session(DBOpen) Then
%>
<H1>Database not connected</H1>
<%
Else
%>
<H1>Database open</H1>
<%
End If
%>
在這些情況下,腳本*能透過將伺服器端腳本寫到一起來,而用Response.write產生Html程式碼來提高*能。比如:
<%
If not Session (DBOpen) Then
Response.Write <H1>Database not connected</H1>
Else
Response.Write <H1>Database open</H1>
End If
%>
在大的腳本和很多腳本的情況下,你將能看到*能的提升。注意這裡盡量避免了使用<%標記,這樣就能提高*能,ASP不需在執行腳本的時候計算字符的Ascii碼。
Session狀態
無庸置疑地,在Asp中能夠透過Session維持某個狀態的能力是十分強大的特色。然而,它會影響你的*能。明顯地,你的網站的可伸縮**變成了另一個問題,如果限制Session的使用的話。然而,session會為每個使用者消耗伺服器資源。
如果你不使用session 變量,或者事實上你不必使用?使用隱藏表單域,在資料庫中保存數據,查詢字串是不是其中的竅門?所以你應該禁止Session狀態。你可以使用下面的聲明禁止使用session:
@EnableSessionState = False
這樣,ASP將不再檢查session資訊。
如果你必須依賴session狀態,應該避免在session物件中存放大量的資料。 IIS中的session在客戶端的HTTP cookie可用的時候就會保持,導致被session佔用的內存在session 終止或逾時前一直被佔用。這樣,如果很多使用者同時使用你的程式的時候,你的伺服器資源可能會耗盡。
資料庫存取
資料庫存取是必須的麻煩?存取資料庫將會激烈地減慢你的程序,但很顯然,如果沒有資料庫,很多網站將變得毫無價值可言。但透過預存程序存取資料庫來取代使用嵌入式Sql 語句,你可以提升潛在的*能。透過使用預存程序和ActiveX Data Objects (ADO),它們也同樣擁有良好的靈活*。盡可能從預存程序來輸出資料。
確認你的資料庫有索引,因為這樣能直接提高你的程式的效率。同樣,盡量在你的資料庫伺服器執行更新統計(Update Statistics) 以幫助追蹤你的資料分發,這樣,你的資料庫就能夠基於這些資訊來改造查詢執行。注意一些資料庫例如MS Access,是不是真正能在企業級程式中接受? SQL Sever 7.0或Oracle是一個更好的賭注。
讓SQL象它被設計的那樣運作,它能count(統計),連接(join),排序(sort)和group 資料。當你能夠寫出一個查詢語句來做這些東西的時候,就不要自己用其它語言來實現。
以下是一個統計所有欄位的最簡單的語法:
SELECT count(*) FROM publishers WHERE state='NY'
如果你統計一個指定的資料列,你必須使用group by語句來分組該列,否則它不會運作:
SELECT count(city),city FROM publishers GROUP BY city
分類回傳的資料:
SELECT * FROM TableName WHERE FieldName>50 OR FieldName<100 ORDER BY FieldName2, Field Name3
使用Odbc還是檔案DSN連接資料庫?使用快速的OLEDB Provider技術連接你的資料庫而不是使用DSN連線。不再需要懇求你的ISP(或資料庫**員/網管)為你建立一個系統DSN,當你移走Web檔案的時候,也不需要改變設定。
OLEDB 介於ODBC層和應用程式之間。在你的ASP 頁面中,ADO介於ODEDB之上的應用程式。你的ADO呼叫先被送到OLEDB,接著被送到ODBC層。然而,你可以直接連接到OLEDB 層,如果你這樣做的話,你就能看到伺服器端*能的提高。然而,怎麼直接連接到OLEDB?
如果你使用SQLServer 7,使用下面的連接程式碼連接資料庫:
strConnString = DSN='';DRIVER={SQL SERVER}; & _
UID=myuid;PWD=mypwd; & _
DATABASE=MyDb;SERVER=MyServer;
最重要的參數是DRIVER=部分。如果你要繞過ODBC而使用透過使用OLEDB 連線SQL Server(這是更快的連線),請使用下面的語法:
strConnString =Provider=SQLOLEDB.1;Password=mypassword; & _
Persist Security Info=True;User ID=myuid; & _
Initial Catalog=mydbname; & _
Data Source=myserver;Connect Timeout=15
有什麼不對的地方嗎?
現在你可能會覺得有點奇怪:我們在這個新的連結方法中的要點是什麼呢?為什麼不使用標準DSN-less/System DSN途徑?呵,根據Wrox 在他的著作《ADO 2.0 Programmer's Reference》中測試的結果表明,如果你使用OLEDB連接和DSN或者DSN-less連接方法比較,你會發現有下面的改進:
*能對比:
SQL Access
OLEDBDSNOLEDBDSN
連接時間:18?82?連接時間:62?99
查詢1,000筆記錄時間:29005400查詢1,000筆記錄時間: 100950
註:這個結果可以在Wrox的《ADO 2.0 Programmer's Reference》一書的第232和233頁查到。時間的單位是毫秒,查詢1,000記錄時間是透過伺服器端遊標計算出來的(當使用客戶端遊標的時候,OLEDB與DSN記錄集的*能之間的差別不大)。
ASP解碼問題:
盡可能在客戶端確認使用者輸入來減少HTTP來回請求的數量。如果瀏覽器有支援JavaScript或其它腳本的能力,請使用它們的力量來釋放更多的伺服器資源。
下面的VBScript運行於客戶端瀏覽器,在提交到你的伺服器之前,用來驗證使用者資訊:
<SCRIPT LANGUAGE=VBScript>
<!--
Sub btnEnter_OnClick
Dim TheForm
Set TheForm = Document.MyForm
If IsNumeric(TheForm.Age.Value) Then
TheForm.submit
Else
Msgbox Please enter a numerical age.
End if
End Sub
//-->
</SCRIPT>
<FORMmethod=POST name=MyFormaction=myfile.asp>? Name: <INPUT typr=text name=Name>
Age: <INPUT type=text name=Age>
<INPUT type=button name=btnEntervalue=Enter>
</FORM>
使用局部變量,避免使用全域變數。局部變數比全域變數更快被Asp 腳本引擎訪問,因為不需搜尋整個名稱域。避免改變數組定義。在第一次初始化的時候就簡單分配足夠的大小效率更高。在這種情況下,你可能會浪費一些內存,但你獲得了速度的優勢。在伺服器負載重的時候這個技術是顯然易見有效的。
如果你需要引用不一定要用到的對象,那麼最好使用<OBJECT>標記而不是用Server.CreateObject方法。 使用Server.CreateObject引起對象立即被建立,反之,<OBJECT>標記則不會這樣立即建立對像如果使用<object>定義後不使用該對象,你不會浪費資源。
例如:下面的例子是一個使用<OBJECT>標記建立一個application-scope廣告輪Ad Rotator物件實
例:
<OBJECT runat=server scope=Application id=MyAds progid=MSWC.AdRotator>
</OBJECT>
在將該Ad Rotator物件儲存到Application後,你可以在任何程式的頁面中用下面的語法存取該對象
<%=MyAds.GetAdvertisement(CustomerAds.txt) %>
打開'Option Explicit'開關。在VB和VBScript 中,你可以在沒有明確宣告的情況下使用變數。但打開這個選項可以鑑別用定義變量,這樣可以很好地書寫變量,並能幫助提高*能。未定義的局部變數會變慢,因為在建立它之前,名稱空間必須被搜遍後才知道變數是否存在。擺脫它,使每個變數清楚定義(先定義後使用)。
這是一個好習慣,it may trap typos, 這樣比較快。
除非你真正需要使用,否而不要使用Server.MapPath方法。如果你知道真實路徑就使用真實路徑。使用MapPath需要IIS找回現時伺服器路徑,這意味著得向伺服器發送一個特殊的請求,也就意味著降低了*能。另一個方法是將路徑存放到局部變數中,並在需要的時候使用,這樣就不需要伺服器多次查找。
檢查你自己是怎麼做的
你可以透過工具來測量你的系統*能,例如係統*能監視器,netMon和PerfMon。測試web*能可以用WCAT (Web Capacity Analysis Tool)。使用WCAT,你可以測試你的IIS伺服器和網路配置回應各種各樣的客戶請求,資料或HTML頁面的能力。這些測試結果能夠被用來作為優化你的伺服器和網路配置的指導。 WCAT是專門設計用來估計Windows 2000中的網際網路服務(或Windows NT)和IIS 能回應的客戶工作量
(仿真)。為了得到更多的信息,請參閱IIS Resource Kit(資源工具包)。那裡也有冗長的WCAT使用者指南在MSDN線上Web sorkshop網站裡面有一個下載連結。如果你認真對待你的Asp *能的話,務必取得該工具。
力求最優化應用程式*能,你的web 應用程式會運作更加順暢。如果不是真正需要,就不要影響伺服器的*能。
asp 以預存程序實現資料分頁
一、建立表tiku_koushi
if exists (select * from dbo.sysobjects where id =
object_id(N'[dbo].[tiku_koushi]') and OBJECTPROPERTY
(id, N'IsUserTable') = 1)
drop table [dbo].[tiku_koushi]
GO
CREATE TABLE [dbo].[tiku_koushi] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[title] [varchar] (250) COLLATE
Chinese_PRC_CI_AS NULL ,
[list2_id] [char] (10) COLLATE
Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
GO
二、預存程序sp_c
CREATE proc sp_c
@tablename varchar(50),
@title varchar(250),
@list2_id varchar(50)
as
if @tablename='tiku_koushi'
select count(*) from tiku_koushi where title like '%'+@title+'%' and list2_id=@list2_id
GO
三、預存程序sp_search_tiku
CREATE PROCEDURE sp_search_tiku
@tablename varchar(50),
@title varchar(250),
@list2_id varchar(10),
@pagesize int,
@page int
AS
if @tablename='tiku_koushi'
begin
declare @ks int
declare @str varchar(200)
set @ks=@pagesize*(@page-1)
if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[temp_table91]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
begin
select * into temp_table91 from tiku_koushi where
title like '%'+@title+'%' and list2_id=@list2_id order
by id desc
set rowcount @pagesize
set @str='select * from temp_table91 where id not in
(select top '+str(@ks)+' id from temp_table91)'
execute(@str)
drop table temp_table91
end
end
GO
四、search_koushi.asp
<!-- #include file=conn.asp -->
<%
line=6
if request(page)= then
page=1
else
page=request(page)
end if
if page<1 then
page=1
end if
title=trim(request(title))
list2_id=trim(request(list2_id))
set rs2=conn.execute(sp_c 'tiku_koushi','&title&','&list2_id&')
pagecount=CInt(rs2(0)/line)
if(CInt(rs2(0)) mod line)=0 then
pagecount=pagecount
else
pagecount=pagecount+1
end if
if CInt(page)>=pagecount then
page=CInt(pagecount)
end if
str=
str=str&page=&page&&title=&title&&list2_id=&list2_id
set rs=conn.execute
(sp_search_tiku 'tiku_koushi','&title&','&list2_id&','&line&','&CInt(page)&')
if rs.eof then
response.write no record
else
%>
<html>
<head>
<style type=text/css>
td{font-size:12px;}
a{text-decoration:none;}
</style>
<script language=javascript>
</script>
</head>
<body>
<table width=518 border=1 bordercolorlight=000000
bordercolordark=#ffffff
align=center cellpadding=0 cellspacing=0>
<!--DWLayoutTable-->
<tr bgcolor=#dfdfdf>
<td width=454 align=center height=24 valign=middle>口試題的題目</td>
<td width=63 align=center valign=middle>刪除</td>
</tr>
<% do until rs.eof %>
<tr height=22>
<td valign=middle>·<a href=void(0)
onclick=window.open('editkoushi.asp?id=<%=rs(id)%>&page=<%=page%>&title=<%=title%>&list2_id=<%=list2_id%>','' ,'width=518
height=160 left=100')>
<%=rs(title)%></a></td>
<td align=center valign=middle>刪除</td>
</tr>
<%
rs.movenext
loop
%>
<tr align=left valign=middle bgcolor=efeff6
height=22>
<td colspan=2 style=padding-left:6px;>
<a href=search_koushi.asp?page=<%=1%>&title=<%=title%>&list2_id=<%=list2_id%>>首頁</a> <a
href=search_koushi.asp?page=<%=page-1%>&title=<%=title%>&list2_id=<%=list2_id%>>上一頁</a> <a
href=search_koushi.asp?page=<%=page+1%>&title=<%=title%>&list2_id=<%=list2_id%>>下一頁</a> <a
href=search_koushi.asp?page=<%=pagecount%>&title=<%=title%>&list2_id=<%=list2_id%>>末頁</a>
共<%=pagecount%> 頁當前頁為: <%=page%>/<%=pagecount%> 頁
共有<%=rs2(0)%> 筆記錄</td>
</tr>
</table>
</body>
</html>
<%
rs2.close
set rs2=nothing
rs.close
set rs=nothing
end if
%>