引言 性能是一個特征。您必須預先設計性能,否則您以后就得重寫應用程序。就是說,有哪些好的策略可使 Active Server Pages (ASP) 應用程序性能達到最佳?
本文介紹了優(yōu)化 ASP 應用程序和 Visual Basic® Scripting Edition (VBScript) 的技巧。本文討論了許多陷阱。本文列出的建議已經(jīng)在 http://www.microsoft.com 和其它站點中進行了測試,效果十分顯著。本文假定您已經(jīng)對 ASP 開發(fā),包括 VBScript 和/或 JScript、ASP Application、ASP Session 和其它 ASP 固有對象(Request、Response 和 Server)有了基本了解。
通常,ASP 性能主要取決于 ASP 代碼本身以外的很多因素。我們不在一篇文章中羅列出所有的信息,在本文結尾處我們列出了與性能有關的資源。這些鏈接涵蓋了 ASP 和非 ASP 主題,包括 ActiveX® 數(shù)據(jù)對象 (ADO)、組件對象模型 (COM)、數(shù)據(jù)庫和 Internet Information Server (IIS) 配置。這些都是我們喜歡的一些鏈接 - 一定要去看看。
技巧 1:將經(jīng)常使用的數(shù)據(jù)緩存在 Web 服務器上 典型的 ASP 頁從后端數(shù)據(jù)存儲中檢索數(shù)據(jù),然后將結果轉換成超文本標記語言 (HTML)。無論數(shù)據(jù)庫的速度如何,從內存中檢索數(shù)據(jù)總要比從后端數(shù)據(jù)存儲中檢索數(shù)據(jù)快得多。從本地硬盤讀取數(shù)據(jù)通常也比從數(shù)據(jù)庫中檢索數(shù)據(jù)更快。因此,通常可以將數(shù)據(jù)緩存在 Web 服務器上(存儲在內存或磁盤中),來提高性能。
緩存是傳統(tǒng)的以空間換取時間的做法。如果您緩存的內容正確,那么您可以看到性能會有顯著的提高。為使緩存有效,必須保存那些經(jīng)常重復使用的數(shù)據(jù),且要重新計算這些數(shù)據(jù)需要(適度)大的開銷。如果緩存的都是些陳舊的數(shù)據(jù),就會造成內存浪費。
不經(jīng)常發(fā)生改變的數(shù)據(jù)是很好的緩存候選數(shù)據(jù),因為您不必擔心隨著時間的遷移該數(shù)據(jù)與數(shù)據(jù)庫同步的問題。組合框列表、引用表、DHTML 碎片、擴展標記語言 (XML) 字符串、菜單項和站點配置變量(包括數(shù)據(jù)源名稱 (DSN)、Internet 協(xié)議 (IP) 地址和 Web 路徑)都是很好的緩存候選內容。注意您可以緩存數(shù)據(jù)的“表示”,而不緩存數(shù)據(jù)本身。如果 ASP 頁很少更改,且緩存的開銷也很大(例如,整個產(chǎn)品目錄),則應考慮事先產(chǎn)生 HTML,而不是在響應每個請求時重新顯示。
應將數(shù)據(jù)緩存在哪里,有哪些緩存策略?通常,數(shù)據(jù)緩存在 Web 服務器的內存或磁盤中。下兩個技巧講述了這兩個方法。
技巧 2: 將經(jīng)常使用的數(shù)據(jù)緩存在 Application 或 Session 對象中 ASP Application 和 Session 對象為將數(shù)據(jù)緩存在內存中提供了方便的容器。您可以將數(shù)據(jù)指派到 Application 和 Session 對象中,這些數(shù)據(jù)在 HTTP 調用之間保留在內存中。Session 數(shù)據(jù)是按每個用戶分別存儲的,而 Application 數(shù)據(jù)則在所有用戶之間共享。
什么時候將數(shù)據(jù)裝載到 Application 或 Session 中呢?通常,數(shù)據(jù)是在啟動 Application 或 Session 時裝載。要在 Application 或 Session 啟動過程中裝載數(shù)據(jù),應將適當?shù)拇a分別添加到 Application_OnStart() 或 Session_OnStart() 中。這些函數(shù)應在 Global.asa 中,如果沒有,則可以添加這些函數(shù)。還可以在第一次需要時裝載該數(shù)據(jù)。為此,在 ASP 頁中添加一些代碼(或編寫一個可重復使用的腳本函數(shù)),以檢查數(shù)據(jù)是否存在,如果不存在,就裝載數(shù)據(jù)。這是一個傳統(tǒng)的性能技術,稱為“惰性計算” - 在您知道需要某一個值以前不計算該值。例如:
<% Function GetEmploymentStatusList Dim d d = Application(?EmploymentStatusList?) If d = ?? Then ' FetchEmploymentStatusList function (not shown) ' fetches data from DB, returns an Array d = FetchEmploymentStatusList() Application(?EmploymentStatusList?) = d End If GetEmploymentStatusList = d End Function %>
可以為所需要的每個數(shù)據(jù)塊編寫類似的函數(shù)。
應以什么格式存儲數(shù)據(jù)?可以存儲任何變體類型,因為所有腳本變量都是變體型。例如,您可以存儲字符串、整數(shù)或數(shù)組。通常,您將以這些變量類型之一存儲 ADO 記錄集的內容。要從 ADO 記錄集獲取數(shù)據(jù),您可以手工將數(shù)據(jù)復制到 VBScript 變量,一次一個字段。使用一個 ADO 記錄集持久函數(shù) GetRows()、GetString() 或 Save()(ADO 2.5),可加快速度且更容易一些。其詳細情況已超出本文所討論的范圍,但下面給出了一個函數(shù)舉例,說明使用 GetRows() 返回記錄集數(shù)據(jù)的一個數(shù)組:
' Get Recordset, return as an Array Function FetchEmploymentStatusList Dim rs Set rs = CreateObject(?ADODB.Recordset?) rs.Open ?select StatusName, StatusID from EmployeeStatus?, _ ?dsn=employees;uid=sa;pwd=;? FetchEmploymentStatusList = rs.GetRows() ? Return data as an Array rs.Close Set rs = Nothing End Function
對上面舉例做更進一步改進,可以將 HTML 緩存為列表,而不是數(shù)組。下面是簡單的示例:
' Get Recordset, return as HTML Option list Function FetchEmploymentStatusList Dim rs, fldName, s Set rs = CreateObject(?ADODB.Recordset?) rs.Open ?select StatusName, StatusID from EmployeeStatus?, _ ?dsn=employees;uid=sa;pwd=;? s = ?<select name=??EmploymentStatus??>? & vbCrLf Set fldName = rs.Fields(?StatusName?) ' ADO Field Binding Do Until rs.EOF ' Next line violates Don't Do String Concats, ' but it's OK because we are building a cache s = s & ? <option>? & fldName & ?</option>? & vbCrLf rs.MoveNext Loop s = s & ?</select>? & vbCrLf rs.Close Set rs = Nothing ' See Release Early FetchEmploymentStatusList = s ' Return data as a String End Function
在適當?shù)臈l件下,可以將 ADO 記錄集本身緩存在 Application 或 Session 作用域中。有兩個警告:
必須將 ADO 標記為自由線程 必須使用斷開連接的記錄集。 如果不能保證滿足這兩個要求,則不要緩存 ADO 記錄集。在下面的“非敏捷組件”和“不要緩存連接”技巧中,我們將討論將 COM 對象存儲在 Application 或 Session 作用域中的危險性。
當您將數(shù)據(jù)存儲在 Application 或 Session 作用域時,數(shù)據(jù)將保留在那里,直到您以編程方式改變它、Session 過期或 Web 應用程序重新啟動為止。如果數(shù)據(jù)需要更新怎么辦?要手工強制對 Application 數(shù)據(jù)進行更新,您可以訪問只有管理員才可訪問的 ASP 頁來更新數(shù)據(jù)。或者,您可以通過函數(shù)定期自動刷新數(shù)據(jù)。下面例子存儲帶有緩存數(shù)據(jù)的時間戳,并隔一段時間后刷新數(shù)據(jù)。
<% ' error handing not shown... Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds
' Function to return the employment status list Function GetEmploymentStatusList UpdateEmploymentStatus GetEmploymentStatusList = Application(?EmploymentStatusList?) End Function
' Periodically update the cached data Sub UpdateEmploymentStatusList Dim d, strLastUpdate strLastUpdate = Application(?LastUpdate?) If (strLastUpdate = ??) Or _ (UPDATE_INTERVAL < DateDiff(?s?, strLastUpdate, Now)) Then
' Note: two or more calls might get in here. This is okay and will simply ' result in a few unnecessary fetches (there is a workaround for this)
' FetchEmploymentStatusList function (not shown) ' fetches data from DB, returns an Array d = FetchEmploymentStatusList()
' Update the Application object. Use Application.Lock() ' to ensure consistent data Application.Lock Application(?EmploymentStatusList?) = Events Application(?LastUpdate?) = CStr(Now) Application.Unlock End If End Sub
請參見 World's Fastest ListBox with Application Data,上面還有一個例子。
要知道在 Session 或 Application 對象中緩存大的數(shù)組不是一個好的做法。在訪問數(shù)組的任何元素之前,腳本語言的語法要求必須臨時復制整個數(shù)組。例如,如果將由字符串組成的有 100,000 個元素的數(shù)組(該數(shù)組將美國郵政編碼映射到當?shù)氐臍庀笳荆┚彺嬖?Application 對象中,ASP 必須先將所有的 100,000 個氣象站復制到臨時數(shù)組中,然后才能提取一個字符串。在這種情況下,用自定義方法建立一個自定義組件來存儲氣象站 - 或使用一個詞典組件會更好。
再警告大家一下,不要將嬰兒與洗澡水一起倒掉:數(shù)組能快速查尋和存儲在內存中是鄰近的關鍵數(shù)據(jù)對。索引一個詞典比索引一個數(shù)組要慢得多。應針對您的實際情況,選擇提供最佳性能的數(shù)據(jù)結構。
|