[p]本文介紹了優(yōu)化 asp 應(yīng)用程序和 visual basic® scripting edition (vbscript) 的技巧。本文討論了許多陷阱。本文列出的建議已經(jīng)在 [url=http://microsoft.com/]http://microsoft.com[/url] 和其它站點(diǎn)中進(jìn)行了測(cè)試,效果十分顯著。本文假定您已經(jīng)對(duì) asp 開(kāi)發(fā),包括 vbscript 和/或 jscript、asp application、asp session 和其它 asp 固有對(duì)象(request、response 和 server)有了基本了解。[/p]
[p]通常,asp 性能主要取決于 asp 代碼本身以外的很多因素。我們不在一篇文章中羅列出所有的信息,在本文結(jié)尾處我們列出了與性能有關(guān)的資源。這些鏈接涵蓋了 asp 和非 asp 主題,包括 activex® 數(shù)據(jù)對(duì)象 (ado)、組件對(duì)象模型 (com)、數(shù)據(jù)庫(kù)和 internet information server (iis) 配置。這些都是我們喜歡的一些鏈接 - 一定要去看看。[/p]
[p][b]技巧 1:將經(jīng)常使用的數(shù)據(jù)緩存在 web 服務(wù)器上[/b][/p]
[p] 典型的 asp 頁(yè)從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù),然后將結(jié)果轉(zhuǎn)換成超文本標(biāo)記語(yǔ)言 (html)。無(wú)論數(shù)據(jù)庫(kù)的速度如何,從內(nèi)存中檢索數(shù)據(jù)總要比從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù)快得多。從本地硬盤(pán)讀取數(shù)據(jù)通常也比從數(shù)據(jù)庫(kù)中檢索數(shù)據(jù)更快。因此,通常可以將數(shù)據(jù)緩存在 web 服務(wù)器上(存儲(chǔ)在內(nèi)存或磁盤(pán)中),來(lái)提高性能。[/p]
[p] 緩存是傳統(tǒng)的以空間換取時(shí)間的做法。如果您緩存的內(nèi)容正確,那么您可以看到性能會(huì)有顯著的提高。為使緩存有效,必須保存那些經(jīng)常重復(fù)使用的數(shù)據(jù),且要重新計(jì)算這些數(shù)據(jù)需要(適度)大的開(kāi)銷(xiāo)。如果緩存的都是些陳舊的數(shù)據(jù),就會(huì)造成內(nèi)存浪費(fèi)。[/p]
[p] 不經(jīng)常發(fā)生改變的數(shù)據(jù)是很好的緩存候選數(shù)據(jù),因?yàn)槟槐負(fù)?dān)心隨著時(shí)間的遷移該數(shù)據(jù)與數(shù)據(jù)庫(kù)同步的問(wèn)題。組合框列表、引用表、dhtml 碎片、擴(kuò)展標(biāo)記語(yǔ)言 (xml) 字符串、菜單項(xiàng)和站點(diǎn)配置變量(包括數(shù)據(jù)源名稱 (dsn)、internet 協(xié)議 (ip) 地址和 web 路徑)都是很好的緩存候選內(nèi)容。注意您可以緩存數(shù)據(jù)的“表示”,而不緩存數(shù)據(jù)本身。如果 asp 頁(yè)很少更改,且緩存的開(kāi)銷(xiāo)也很大(例如,整個(gè)產(chǎn)品目錄),則應(yīng)考慮事先產(chǎn)生 html,而不是在響應(yīng)每個(gè)請(qǐng)求時(shí)重新顯示。[/p]
[p] 應(yīng)將數(shù)據(jù)緩存在哪里,有哪些緩存策略?通常,數(shù)據(jù)緩存在 web 服務(wù)器的內(nèi)存或磁盤(pán)中。下兩個(gè)技巧講述了這兩個(gè)方法。[/p]
[p][b]技巧 2: 將經(jīng)常使用的數(shù)據(jù)緩存在 application 或 session 對(duì)象中[/b][/p]
[p] asp application 和 session 對(duì)象為將數(shù)據(jù)緩存在內(nèi)存中提供了方便的容器。您可以將數(shù)據(jù)指派到 application 和 session 對(duì)象中,這些數(shù)據(jù)在 http 調(diào)用之間保留在內(nèi)存中。session 數(shù)據(jù)是按每個(gè)用戶分別存儲(chǔ)的,而 application 數(shù)據(jù)則在所有用戶之間共享。[/p]
[p] 什么時(shí)候?qū)?shù)據(jù)裝載到 application 或 session 中呢?通常,數(shù)據(jù)是在啟動(dòng) application 或 session 時(shí)裝載。要在 application 或 session 啟動(dòng)過(guò)程中裝載數(shù)據(jù),應(yīng)將適當(dāng)?shù)拇a分別添加到 application_onstart() 或 session_onstart() 中。這些函數(shù)應(yīng)在 global.asa 中,如果沒(méi)有,則可以添加這些函數(shù)。還可以在第一次需要時(shí)裝載該數(shù)據(jù)。為此,在 asp 頁(yè)中添加一些代碼(或編寫(xiě)一個(gè)可重復(fù)使用的腳本函數(shù)),以檢查數(shù)據(jù)是否存在,如果不存在,就裝載數(shù)據(jù)。這是一個(gè)傳統(tǒng)的性能技術(shù),稱為“惰性計(jì)算” - 在您知道需要某一個(gè)值以前不計(jì)算該值。例如:[/p]
[p]<% [br]function getemploymentstatuslist [br]dim d [br]d = application(?employmentstatuslist?) [br]if d = ?? then [br]' fetchemploymentstatuslist function (not shown) [br]' fetches data from db, returns an array [br]d = fetchemploymentstatuslist() [br]application(?employmentstatuslist?) = d [br]end if [br]getemploymentstatuslist = d [br]end function [br]%>[/p]
[p] 可以為所需要的每個(gè)數(shù)據(jù)塊編寫(xiě)類似的函數(shù)。[/p]
[p] 應(yīng)以什么格式存儲(chǔ)數(shù)據(jù)?可以存儲(chǔ)任何變體類型,因?yàn)樗心_本變量都是變體型。例如,您可以存儲(chǔ)字符串、整數(shù)或數(shù)組。通常,您將以這些變量類型之一存儲(chǔ) ado 記錄集的內(nèi)容。要從 ado 記錄集獲取數(shù)據(jù),您可以手工將數(shù)據(jù)復(fù)制到 vbscript 變量,一次一個(gè)字段。使用一個(gè) ado 記錄集持久函數(shù) getrows()、getstring() 或 save()(ado 2.5),可加快速度且更容易一些。其詳細(xì)情況已超出本文所討論的范圍,但下面給出了一個(gè)函數(shù)舉例,說(shuō)明使用 getrows() 返回記錄集數(shù)據(jù)的一個(gè)數(shù)組:[/p]
[p]' get recordset, return as an array [br]function fetchemploymentstatuslist [br]dim rs [br]set rs = createobject(?adodb.recordset?) [br]rs.open ?select statusname, statusid from employeestatus?, _ [br]?dsn=employees;uid=sa;pwd=;? [br]fetchemploymentstatuslist = rs.getrows() ? return data as an array [br]rs.close [br]set rs = nothing [br]end function[/p]
[p][br] 對(duì)上面舉例做更進(jìn)一步改進(jìn),可以將 html 緩存為列表,而不是數(shù)組。下面是簡(jiǎn)單的示例:[/p]
[p]' get recordset, return as html option list [br]function fetchemploymentstatuslist [br]dim rs, fldname, s [br]set rs = createobject(?adodb.recordset?) [br]rs.open ?select statusname, statusid from employeestatus?, _ [br]?dsn=employees;uid=sa;pwd=;? [br]s = ?
? & vbcrlf [br]rs.close [br]set rs = nothing ' see release early [br]fetchemploymentstatuslist = s ' return data as a string [br]end function[/p]
[p] 在適當(dāng)?shù)臈l件下,可以將 ado 記錄集本身緩存在 application 或 session 作用域中。有兩個(gè)警告:[/p]
[p] 必須將 ado 標(biāo)記為自由線程 [br] 必須使用斷開(kāi)連接的記錄集。 [br] 如果不能保證滿足這兩個(gè)要求,則不要緩存 ado 記錄集。在下面的“非敏捷組件”和“不要緩存連接”技巧中,我們將討論將 com 對(duì)象存儲(chǔ)在 application 或 session 作用域中的危險(xiǎn)性。[/p]
[p] 當(dāng)您將數(shù)據(jù)存儲(chǔ)在 application 或 session 作用域時(shí),數(shù)據(jù)將保留在那里,直到您以編程方式改變它、session 過(guò)期或 web 應(yīng)用程序重新啟動(dòng)為止。如果數(shù)據(jù)需要更新怎么辦?要手工強(qiáng)制對(duì) application 數(shù)據(jù)進(jìn)行更新,您可以訪問(wèn)只有管理員才可訪問(wèn)的 asp 頁(yè)來(lái)更新數(shù)據(jù)?;蛘?,您可以通過(guò)函數(shù)定期自動(dòng)刷新數(shù)據(jù)。下面例子存儲(chǔ)帶有緩存數(shù)據(jù)的時(shí)間戳,并隔一段時(shí)間后刷新數(shù)據(jù)。[/p]
[p]<% [br]' error handing not shown... [br]const update_interval = 300 ' refresh interval, in seconds [br][br]' function to return the employment status list [br]function getemploymentstatuslist [br]updateemploymentstatus [br]getemploymentstatuslist = application(?employmentstatuslist?) [br]end function [br][br]' periodically update the cached data [br]sub updateemploymentstatuslist [br]dim d, strlastupdate [br]strlastupdate = application(?lastupdate?) [br]if (strlastupdate = ??) or _ [br](update_interval < datediff(?s?, strlastupdate, now)) then [br][br]' note: two or more calls might get in here. this is okay and will simply [br]' result in a few unnecessary fetches (there is a workaround for this) [br][br]' fetchemploymentstatuslist function (not shown) [br]' fetches data from db, returns an array [br]d = fetchemploymentstatuslist() [br][br]' update the application object. use application.lock() [br]' to ensure consistent data [br]application.lock [br]application(?employmentstatuslist?) = events [br]application(?lastupdate?) = cstr(now) [br]application.unlock [br]end if [br]end sub[/p]
[p] 請(qǐng)參見(jiàn) world's fastest listbox with application data,上面還有一個(gè)例子。[/p]
[p] 要知道在 session 或 application 對(duì)象中緩存大的數(shù)組不是一個(gè)好的做法。在訪問(wèn)數(shù)組的任何元素之前,腳本語(yǔ)言的語(yǔ)法要求必須臨時(shí)復(fù)制整個(gè)數(shù)組。例如,如果將由字符串組成的有 100,000 個(gè)元素的數(shù)組(該數(shù)組將美國(guó)郵政編碼映射到當(dāng)?shù)氐臍庀笳荆┚彺嬖?application 對(duì)象中,asp 必須先將所有的 100,000 個(gè)氣象站復(fù)制到臨時(shí)數(shù)組中,然后才能提取一個(gè)字符串。在這種情況下,用自定義方法建立一個(gè)自定義組件來(lái)存儲(chǔ)氣象站 - 或使用一個(gè)詞典組件會(huì)更好。[/p]
[p] 再警告大家一下,不要將嬰兒與洗澡水一起倒掉:數(shù)組能快速查尋和存儲(chǔ)在內(nèi)存中是鄰近的關(guān)鍵數(shù)據(jù)對(duì)。索引一個(gè)詞典比索引一個(gè)數(shù)組要慢得多。應(yīng)針對(duì)您的實(shí)際情況,選擇提供最佳性能的數(shù)據(jù)結(jié)構(gòu)。[/p]
[p]#p#[/p]
[p][b]技巧 3:將數(shù)據(jù)和 html 緩存在 web 服務(wù)器的磁盤(pán)上[/b][/p]
[p] 有時(shí),數(shù)據(jù)可能太多,無(wú)法都緩存在內(nèi)存中。“太多”只是一個(gè)說(shuō)法,這要看您想消耗多少內(nèi)存,以及需緩存的項(xiàng)目數(shù)和檢索這些項(xiàng)目的頻率。在任何情況下,如果數(shù)據(jù)太多而無(wú)法都緩存在內(nèi)存中,則考慮將數(shù)據(jù)以文本或 xml 文件緩存在 web 服務(wù)器的硬盤(pán)上??梢酝瑫r(shí)將數(shù)據(jù)緩存在磁盤(pán)和內(nèi)存中,為您的站點(diǎn)建立最適宜的緩存策略。[/p]
[p] 注意當(dāng)測(cè)量單個(gè) asp 頁(yè)的性能時(shí),檢索磁盤(pán)上的數(shù)據(jù)可能不一定要比從數(shù)據(jù)庫(kù)檢索數(shù)據(jù)更快。但緩存會(huì)降低數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)上的負(fù)載。在高負(fù)載的情況下,這樣做可大大改善總體吞吐量。當(dāng)緩存開(kāi)銷(xiāo)很大的查詢結(jié)果(如多表聯(lián)接或復(fù)合存儲(chǔ)過(guò)程)或大的結(jié)果集時(shí),這是非常有效的。與往常一樣,要測(cè)試一下幾種方案的優(yōu)劣。[/p]
[p] asp 和 com 提供一些建立基于磁盤(pán)的緩存方案的工具。ado 記錄集 save() 和 open() 函數(shù)保存和裝載磁盤(pán)中的記錄集??梢允褂眠@些方法重新編寫(xiě)上面 application 數(shù)據(jù)緩存技巧中的代碼示例,用文件的 save() 代替寫(xiě)到 application 對(duì)象中的代碼。[/p]
[p] 有一些其它組件可以用于文件:[/p]
[p] scripting.filesystemobject 可使您創(chuàng)建、讀和寫(xiě)文件。 [br] 與 internet explorer 一起提供的 microsoft® xml 解析器 (msxml) 支持保存和裝載 xml 文檔。 [br] lookuptable 對(duì)象(例如,用在 msn 上)是從磁盤(pán)裝載簡(jiǎn)單列表的最好選擇。 [br] 最后,應(yīng)考慮將數(shù)據(jù)的表示緩存在磁盤(pán)上,而不是數(shù)據(jù)本身。預(yù)先轉(zhuǎn)換的 html 可以用 .htm 或 .asp 文件存儲(chǔ)在磁盤(pán)上,超級(jí)鏈接可以直接指向這些文件??梢允褂蒙逃霉ぞ撸?xbuilder,或 microsoft® sql server™ internet 發(fā)布功能將產(chǎn)生 html 的過(guò)程自動(dòng)化。或者,您可以將 html 代碼片斷放在 .asp 文件中。還可以使用 filesystemobject 從磁盤(pán)讀取 html 文件,或使用 xml 盡早轉(zhuǎn)換。[/p]
[p][b]技巧 4:避免將非敏捷的組件緩存在 application 或 session 對(duì)象中[/b][/p]
[p] 盡管將數(shù)據(jù)緩存在 application 或 session 對(duì)象中是一個(gè)好的做法,但緩存 com 對(duì)象卻有嚴(yán)重的陷阱。通常,人們傾向于將經(jīng)常使用的 com 對(duì)象緩存到 application 或 session 對(duì)象中。很遺憾,許多 com 對(duì)象(包括所有以 visual basic 6.0 或更低版本編寫(xiě)的對(duì)象)當(dāng)存儲(chǔ)在 application 或 session 對(duì)象時(shí),會(huì)引起嚴(yán)重的瓶頸。[/p]
[p] 具體來(lái)講,當(dāng)任何不敏捷的組件緩存在 session 或 application 對(duì)象時(shí),將引起性能瓶頸。敏捷的組件是被標(biāo)記為 threadingmodel=both 的組件,它聚集 free-threaded marshaler (ftm);或被標(biāo)記為 threadingmodel=neutral 的組件。(neutral 模型是 windows® 2000 和 com+ 的新增模型。) 下列組件不是敏捷的:[/p]
[p] 自由線程的組件(除非它們聚集 ftm)。 [br] 單元線程組件。 [br] 單線程組件。 [br] 配置的組件(microsoft transaction server (mts)/com+ 庫(kù)和服務(wù)器程序包/應(yīng)用程序)不是敏捷的,除非它們是 neutral 線程。單元線程組件和其它非敏捷的組件在頁(yè)作用域內(nèi)是最適合的(即,它們?cè)趩蝹€(gè) asp 頁(yè)上創(chuàng)建和銷(xiāo)毀)。[/p]
[p] 在 iis 4.0 中,被標(biāo)記為 threadingmodel=both 的組件被認(rèn)為是敏捷的。在 iis 5.0 中,只有這一點(diǎn)還不夠。組件必須不僅被標(biāo)記 both,還必須聚集 ftm。有關(guān)敏捷性的文章講述了如何使以 active template library 編寫(xiě)的 c++ 組件聚集 ftm。要注意如果組件緩存界面指針,那么那些指針本身必須是敏捷的,或必須存儲(chǔ)在 com 共用界面表 (git) 中。如果您不能重新編譯 both 線程組件以聚集 ftm,那么您可以將組件標(biāo)記為 threadingmodel=neutral?;蛘撸绻幌胱?iis 執(zhí)行敏捷性檢查(因此,您可以允許非敏捷的組件存儲(chǔ)在 application 或 session 作用域中),您可以在配置數(shù)據(jù)庫(kù)中將 asptrackthreadingmodel 設(shè)置為 true。不建議更改 asptrackthreadingmodel。[/p]
[p] 如果您想將以 server.createobject 創(chuàng)建的非敏捷的組件存儲(chǔ)在 application 對(duì)象中,iis 5.0 將出現(xiàn)一個(gè)錯(cuò)誤。您可以在 global.asa 中使用