ASP.NET性能優化之編碼篇
下面介紹如何在編碼方面進行ASP.NET性能優化。
ASP.NET性能優化之頁面和服務器控件處理
a) 避免到服務器的不必要的往返行程
在某些情況下不必使用 ASP.NET 服務器控件和執行回發事件處理。例如,在 ASP.NET 網頁中驗證用戶輸入經常可在數據提交到服務器之前在客戶端進行。通常,如果不需要將信息傳遞到服務器以進行驗證或將其寫入數據存儲區,請避免使用導致到服務器的往返行程的代碼,這樣可以提高頁的性能并改善用戶體驗。您也可以不執行整個往返行程,而是使用客戶端回調從服務器中讀取數據。
頁面類實現ICallbackEventHandler接口,注冊GetCallbackEventReference方法,也就是ajax的回調實現。
針對一次需要載入很多控件的頁面(載入比較耗時的頁面),我們可以使用ajax技術來達到一定的頁面訪問性能提升。
b) 使用 Page 對象的 IsPostBack 屬性來避免對往返行程執行不必要的處理
如果您編寫處理服務器控件回發處理的代碼,有時可能需要代碼僅在***請求頁時執行,而不是每次回發時都執行。根據該頁是否是響應服務器控件事件生成的,使用 IsPostBack 屬性有條件地執行代碼。
將僅需要***請求頁面時執行的代碼放在IsPostBack條件中運行。
c) 只在必要時保存服務器控件視圖狀態
自動視圖狀態管理使服務器控件可以在往返行程中重新填充它們的屬性值,而您不需要編寫任何代碼。但是,因為服務器控件的視圖狀態在隱藏的窗體字段中往返于服務器,所以該功能影響性能。了解在哪些情況下視圖狀態會有所幫助,在哪些情況下它影響頁的性能,這樣是有幫助的。例如,如果您將服務器控件綁定到每個往返行程上的數據,因為控件的值會在數據綁定期間用新值替換,所以保存的視圖狀態沒有用處。在這種情況下,禁用視圖狀態可以節省處理時間并減少頁的大小。
默認情況下,為所有服務器控件啟用視圖狀態。若要禁用它,請將控件的 EnableViewState 屬性設置為 false。
還可以使用 @ Page 指令禁用整個頁的視圖狀態。當您不從頁回發到服務器時,這將十分有用。
@ Control 指令中還支持 EnableViewState 屬性以指定是否為用戶控件啟用視圖狀態。
查看視圖狀態的方法:
若要分析服務器控件在頁中使用的視圖狀態的大小,請通過將 trace="true" 屬性包含在 @ Page 指令中啟用對該頁的跟蹤。然后在跟蹤輸出中,查看“控件層次結構”表的“Viewstate”列。
下面情況基本上可以禁用viewstate:
(1)頁面控件 (.ascx)
(2)頁面不回傳給自身。
(3)無需對控件的事件處理。
(4)控件沒有動態的或數據綁定的屬性值(或對于每個postpack都在代碼中處理)
d) 除非有特殊的原因要關閉緩沖,否則使其保持打開狀態
禁用 ASP.NET 網頁的緩沖會導致大量的性能開銷。
e) Server.Transfer和Response.Redirect的選擇
Response.Redirect 簡單地告訴瀏覽器訪問另一個頁面。Server.Transfer 有利于減少服務器請求,保持地址欄 URL 不變,允許你將 query string 和 form 變量傳遞到另一個頁面,可以隱藏url中傳遞的參數。
Response.Redirect可以跨站點跳轉,Server.Transfer只能同站點跳轉。
微軟建議:
使用 Transfer Server 對象或跨頁發送的方法在同一個應用程序中的不同 ASP.NET 頁之間重定向
如無特殊要求,應優先選擇Server.Transfer進行頁面跳轉
ASP.NET性能優化之數據訪問
a) 將 SQL Server 和存儲過程用于數據訪問
在 .NET Framework 提供的所有數據訪問方法中,使用 SQL Server 進行數據訪問是生成高性能、可縮放 Web 應用程序的推薦選擇。使用托管 SQL Server 提供程序時,可通過盡可能使用編譯的存儲過程而不是 SQL 命令獲得額外的性能提高。
(僅針對數據庫選擇SQL Server,數據庫為其他的可以忽略此選項)
b) 將 SqlDataReader 類用于快速只進數據游標
SqlDataReader 類提供了從 SQL Server 數據庫檢索的只進數據流。如果您可以在 ASP.NET 應用程序中使用只讀流,則 SqlDataReader 類提供比 DataSet 類更高的性能。SqlDataReader 類使用 SQL Server 的本機網絡數據傳輸格式從數據庫連接直接讀取數據。例如,當綁定到 SqlDataSource 控件時,通過將 DataSourceMode 屬性設置為 DataReader,您將獲得更好的性能。(使用數據讀取器會導致某些功能的丟失。)另外,SqlDataReader 類實現 IEnumerable 接口,該接口也使您可以將數據綁定到服務器控件。
(僅針對數據庫選擇SQL Server,數據庫為其他的可以忽略此選項) MySql中對應MySqlDataReader,根據需要選擇。
c) 盡可能緩存數據和頁輸出
ASP.NET 提供了一些機制,它們會在不需要為每個頁請求動態計算頁輸出或數據時緩存這些頁輸出或數據。另外,通過設計要進行緩存的頁和數據請求(特別是在站點中預期將有較大通訊量的區域),可以優化這些頁的性能。與使用 .NET Framework 的任何其他功能相比,適當地使用緩存可以更好地提高站點的性能。
在使用 ASP.NET 緩存時,應注意以下事項。首先,不要緩存太多項。緩存每個項都有內存開銷。不要緩存容易重新計算和很少使用的項。其次,給緩存項分配的有效期不要太短。很快到期的項會導致緩存中不必要的周轉,并且會導致額外的代碼清除和垃圾回收工作。使用與“ASP.NET Applications”性能對象關聯的“Cache Total Turnover Rate”(緩存總流通率)性能計數器,您可以監視緩存中由于項到期而導致的周轉。高周轉率可能說明存在問題,特別是當項在到期前被移除時。(這種情況有時稱作內存壓力。)
可以考慮把靜態的、變化不大的或者不經常變化需要動態加載的內容放入控件中,使用緩存技術。
< %@ OutputCache Duration="100" VaryByParam="none" %>
d) 適當地使用 SQL 緩存依賴項
ASP.NET 同時支持基于表的輪詢和查詢通知,具體取決于所使用的 SQL Server 的版本。所有 SQL Server 版本都支持基于表的輪詢。在基于表的輪詢中,如果表中的任何內容發生更改,所有偵聽器都會失效。這可能導致應用程序中不必要的改動。建議不要將基于表的輪詢用于具有許多頻繁更改的表。例如,建議將基于表的輪詢用于很少更改的目錄表。建議不要將基于表的輪詢用于訂單表,訂單表具有更頻繁的更新。SQL Server 2005 支持查詢通知。查詢通知支持特定查詢,從而減少在表更改時發送的通知數量。雖然它比基于表的輪詢提供更好的性能,但是它無法擴展到適應數千個查詢。
(僅針對數據庫選擇SQL Server,數據庫為其他的可以忽略此選項)
e) 使用數據源分頁和排序而不是 UI(用戶界面)分頁和排序
DetailsView 和 GridView 等數據控件的 UI 分頁功能可用于支持 ICollection 接口的任何數據源對象。對于每個分頁操作,數據控件查詢數據源的整個數據集并選擇要顯示的行,并放棄其余的數據。如果數據源實現 DataSourceView 并且 CanPage 屬性返回 true,則數據控件將使用數據源分頁而不是 UI 分頁。在這種情況下,數據控件僅查詢每個分頁操作需要的行。因此,數據源分頁比 UI 分頁更高效。只有 ObjectDataSource 數據源控件才支持數據源分頁。若要在其他數據源控件上啟用數據源分頁,必須從該數據源控件繼承并修改其行為。
f) 平衡事件驗證的安全性受益和性能開銷
從 System.Web.UI.WebControls 和 System.Web.UI.HtmlControls 類派生的控件可以驗證事件是否源自該控件所呈現的用戶界面。這樣有助于防止控件響應偽造的事件通知。例如,DetailsView 控件可以防止 Delete(刪除)調用(控件中本質上不支持該調用)的處理以及被操縱而刪除數據。此驗證會帶來一定的性能開銷。可以使用 EnableEventValidation 配置元素和 RegisterForEventValidation 方法控制此行為。驗證的開銷取決于頁上的控件數量,并在幾個百分點范圍內。
強烈建議不要禁用事件驗證。在禁用事件驗證之前,應該確保不會構造任何可能對應用程序具有意外影響的回發。
g) 除非必要,否則避免使用視圖狀態加密
視圖狀態加密會阻止用戶能夠讀取隱藏視圖狀態字段中的值。典型情況是在 DataKeyNames 屬性中帶有一個標識符字段的 GridView 控件。標識符字段是協調對記錄的更新所必需的。由于不想要標識符對用戶可見,因此可以加密視圖狀態。但是,加密對于初始化具有恒定的性能開銷,并具有取決于被加密的視圖狀態大小的附加開銷。加密為每次頁加載而設置,因此在每次頁加載時都會發生相同的性能影響。
h) 使用 SqlDataSource 緩存、排序和篩選
如果 SqlDataSource 控件的 DataSourceMode 屬性設置為 DataSet,則 SqlDataSource 能夠緩存查詢產生的結果集。如果以這種方式緩存數據,則控件的篩選和排序操作(使用 FilterExpression 和 SortParameterName 屬性進行配置)將使用緩存的數據。在許多情況下,如果緩存整個數據集,并使用 FilterExpression 和 SortParameterName 屬性進行排序和篩選,而不是使用帶“WHERE”和“SORT BY”子句的 SQL 查詢(對于這些查詢,每個選擇操作都要訪問數據庫),應用程序會運行得更快。
(根據具體控件,僅支持部分數據庫)
ASP.NET性能優化之編碼實踐
a) 不要依賴代碼中的異常
異常會大大地降低性能,所以您應該避免將它們用作控制正常程序流的方式。如果有可能檢測到代碼中可能導致異常的狀態,請執行這種操作,而不要捕捉異常本身和處理該狀態。常見的代碼檢測方案包括:檢查 null,將一個值分配給將分析為數值的 String,或在應用數學運算前檢查特定值。
b) 在托管代碼中重寫調用密集型的 COM 組件
.NET Framework 提供了一個簡單的方法與傳統的 COM 組件進行交互。其優點是可以在保留現有 COM 組件投資的同時利用 .NET 的功能。但是在某些情況下,保留舊組件的性能開銷使得將組件遷移到托管代碼是值得的。每一情況都是不一樣的,決定是否需要遷移組件的***方法是對網站運行性能測量。建議研究一下如何將經常調用的任何 COM 組件遷移到托管代碼。
許多情況下不可能將舊式組件遷移到托管代碼,特別是在最初遷移 Web 應用程序時。在這種情況下,***的性能障礙之一是將數據從非托管環境封送到托管環境。因此,在交互操作中,請在任何一端執行盡可能多的任務,然后進行單個調用而不是一系列小調用。例如,公共語言運行庫中的所有字符串都是 Unicode 的,所以應在調用托管代碼之前將組件中的所有字符串轉換成 Unicode 格式。
一旦處理完任何 COM 對象或本機資源就釋放它們。這樣,其他請求就能夠使用它們,并且***限度地減少了因稍后請求垃圾回收器釋放它們所引起的性能問題。
c) 避免單線程單元 (STA) COM 組件
默認情況下,ASP.NET 不允許 STA COM 組件在頁內運行。若要運行它們,必須在 .aspx 文件內將 ASPCompat=true 屬性包含在 @ Page 指令中。這樣就將頁執行用的線程池切換到 STA 線程池,而且使 HttpContext 和其他內置對象可用于 COM 對象。避免使用 STA COM 組件是一種性能優化,因為它避免了將多線程單元 (MTA) 封送到 STA 線程的任何調用。
如果必須使用 STA COM 組件,則應避免在執行期間進行大量調用,并嘗試在每次調用期間發送盡可能多的信息。***機制是推遲對象的創建,推薦的做法是僅在需要時或者在 Page_Load 方法中構造 COM 組件和外部資源。
永遠不要將 STA COM 組件存儲在可以由構造它們的線程以外的其他線程訪問的共享資源(如緩存或會話狀態)里。即使 STA 線程調用 STA COM 組件,也只有構造此 STA COM 組件的線程能夠為該調用服務,而這要求封送處理對創建者線程的調用。此封送處理可能產生重大的性能損失和可伸縮性問題。在這種情況下,請考慮使 COM 組件成為 MTA COM 組件或在托管代碼中重寫該組件。
本文來自shllove的專欄:《ASP.NET性能優化整理》。【編輯推薦】