天天看點

ASP.NET性能優化探讨

資料庫優化

要提高資料庫的運作效率,需要從資料庫系統級、資料庫設計級、程式實作級這三方面優化。

1、資料庫系統級

主要是資料庫的運作伺服器硬體條件。選取高性能的伺服器。

2、資料庫設計級

(1) 在資料庫實體設計時,合理設計主鍵與外鍵,适當增加資料備援, 少用觸發器, 多用存儲過程。

(2) 計算非常複雜、而且記錄條數非常巨大時(千萬條級别),要先在資料庫外面以檔案系統方式計算處理完成之後入庫追加到資料表。

(3)資料表記錄太多,超過一千萬條,則要對該表進行水準分割。水準分割的做法是,以該表主鍵PK的某個值為界線,将該表的記錄水準分割為兩個表。若發現某個表的字段太多,例如超過七十個,則垂直分割該表,将原來的一個表分解為兩個表。

(4) 對資料庫管理系統DBMS進行系統優化,即優化各種系統參數,如緩沖區個數。

(5) 在使用面向資料的SQL語言進行程式設計時,盡量采取優化算法。

3、程式實作級

(1)合理掌控資料庫的連接配接和關閉,資料操作完畢關閉資料庫連接配接。

  通路資料庫資源需要建立連接配接、打開連接配接和關閉連接配接幾個操作。這些過程需要多次與資料庫交換資訊以通過身份驗證,比較耗費伺服器資源。Asp.Net中提供了連接配接池(Connection Pool)改善打開和關閉資料庫對性能的影響。系統将使用者的資料庫連接配接放在連接配接池中,需要時取出,關閉時收回連接配接,等待下一次的連接配接請求。

連接配接池的大小是有限的,如果在連接配接池達到最大限度後仍要求建立連接配接,必然大大影響性能。是以,在建立資料庫連接配接後隻有在真正需要操作時才打開連接配接,使用完畢後馬上關閉,進而盡量減少資料庫連接配接打開的時間,避免出現超出連接配接限制的情況。

        SqlConnection conn = new SqlConnection();

        con.ConnectionString = "server=localhost;database=Northwind.mdf;uid=sa;pwd=sa";

        string sql = "select top 5 CustomerID,CompanyName from Customers order by CustomerID";

        SqlCommand cmd = new SqlCommand(sql, con);

        //其他操作代碼;

        conn.Open();

        //其他操作代碼;

        SqlDataReader dr = cmd.ExecuteReader();

        while (dr.Read())

        {

            //相關操作代碼;

        }

        conn.close();

(2)盡量使用存儲過程,并優化查詢語句。

  盡量使用存儲過程,存儲過程是存儲在伺服器上的一組預編譯的SQL語句,類似于DOS系統中的批處理檔案。存儲過程具有對資料庫立即通路的功能,資訊處理極為迅速。使用存儲過程可以避免對指令的多次編譯,在執行一次後其執行規劃就駐留在高速緩存中,以後需要時隻需直接調用緩存中的二進制代碼即可。另外,存儲過程在伺服器端運作,獨立于ASP.NET程式,便于修改,最重要的是它可以減少資料庫操作語句在網絡中的傳輸。

  優化查詢語句,ASP.NET中ADO連接配接消耗的資源相當大,SQL語句運作的時間越長,占用系統資源的時間也越長。是以,盡量使用優化過的SQL語句以減少執行時間。比如,不在查詢語句中包含子查詢語句,充分利用索引等。在實際的編碼中少用"select * from table",而更根據實際需求有選擇篩選字段,減少傳回字段和資料量,減輕資料庫壓力。

(3)合理使用SqlDataReader和DataSet 。對于隻讀資料通路用SqlDataReader,不要使用DataSet,

SqlDataReader類提供了一種讀取從SQL Server 資料庫檢索的隻進資料流的方法。如果當建立 ASP.NET 應用程式時出現允許您使用它的情況,則 SqlDataReader類提供比 DataSet類更高的性能。情況之是以這樣,是因為 SqlDataReader使用SQL Server 的本機網絡資料傳輸格式從資料庫連接配接直接讀取資料。另外,SqlDataReader類實作 IEnumerable接口,該接口也允許您将資料綁定到伺服器控。

DataSet作為一個功能強大的支援離線的資料庫,其對性能的開銷也相對較大SqlDataReader優點:讀取資料非常快。如果對傳回的資料不需做大量處理的情況下,建議使用SqlDataReader,其性能要比DataSet好很多,但缺點是:直到資料讀完才可Close()資料庫的連接配接。DataSet是把資料讀出,緩存在記憶體中缺點:對記憶體的占用較高如果對傳回的資料需做大量的處理用DataSet比較好些,可以減少對資料庫的連接配接操作。優點:隻需連接配接一次就可Close()資料庫的連接配接。

一般情況下,讀取大量資料,對傳回資料不做大量處理用SqlDataReader.對傳回資料大量處理用DataSet比較合适。對SqlDataReader和DataSet的選擇完全取決于程式功能的實作要求。

(4)無論SqlDataReader和DataSet,傳回多個結果集,然後用rd.NextResult() 或ds.Tables[i]來分别處理資料,可以減少重複連接配接資料庫的次數,同時盡量用比較高效的SQL代替後續複雜的DataSet二次加工。

程式設計優化

程式設計優化需要在系統的架構、代碼可讀和效率、頁面以及其他非代碼元素三方面進行。

1、系統的架構

采用合理優秀的架構設計系統,并不是一味的求新或者是追求邏輯上的複雜程度。

2、代碼可讀和效率

(1)使用值類型的ToString()方法。在連接配接字元串時,經常使用"+"符号直接将數字添加到字元串中這種方法雖然簡單,也可以得到正确結果,但是由于涉及到不同的資料類型,數字需要通過裝箱操作轉化為引用類型才可以添加到字元串中。但是裝箱操作對性能影響較大,因為在進行這類處理時,将在托管堆中配置設定一個新的對象,原有的值複制到新建立的對象中。使用值類型的ToString()方法可以避免裝箱操作,進而提高應用程式性能。

(2)運用StringBuilder類。String類的對象是不可改變的,對于String對象的重新指派在本質上是重新建立了一個String對象并将新值賦予該對象。在處理字元串時,最好使用StringBuilder類,其.NET命名空間是System.Text該類并非建立新的對象,而是通過Append,Remove,Insert等方法直接對字元串進行操作,通過ToString()方法傳回操作結果,對性能的提高并非很顯著。

(3)使用 HttpServerUtility.Transfer() 方法在同一應用程式的頁面間重定向,采用 Server.Transfer() 文法,在頁面中使用該方法可避免不必要的用戶端重定向Response.Redirect()

(4)避免使用ArrayList

因為任何對象添加到ArrayList都要封箱為System.Object類型,從ArrayList取出資料時,要拆箱回實際的類型。建議使用自定義的集合類型代替ArrayList。ASP.NET 2.0提供了一個新的類型,叫泛型,這是一個強類型,使用泛型集合就可以避免了封箱和拆箱的發生,提高了性能。(有關泛型資料請參考http://msdn.microsoft.com/zh-cn/library/d5x73970(VS.80).aspx)

(5)使用Hashtable代替其他字典集合類型(如System.Collections.Specialized.StringDictionary , System.Collections.Specialized.NameValueCollection ,System.Collections.Specialized.StringCollection),存放少量資料的時候可以使用Hashtable.

(6)為字元串容器聲明常量,不要直接把字元封裝在雙引号" "裡面。

//避免

MyObject obj = new MyObject();

obj.Status = "ACTIVE";

//推薦

const string C_STATUS = "ACTIVE";

MyObject obj = new MyObject();

obj.Status = C_STATUS;

(7)不要用ToUpper() ToLower()轉換字元串進行比較,用string.Compare()代替,它可以忽略大小寫進行比較。

const string C_VALUE = "COMPARE";

if (String.Compare(sVariable, C_VALUE, true) == 0)

{

Console.Write("相同");

}

也可以用str == String.Empty或者str.Length == 0判斷是否為空,将String對象的Length屬性與0比較是最快的方法,避免不必要的調用 ToUpper()或 ToLower()方法。

(8)類型轉化Int32.TryParse()優于Int32.Parse()優于Convert.ToInt32()。

//推薦

.NET1.1下用Int32.Parse();.NET2.0用Int32.TryParse()

因為:

Convert.ToInt32 會把最終的解析工作代理給 Int32.Parse

Int32.Parse 會把最終的解析工作代理給 Number.ParseInt32

Int32.TryParse 會把最終的解析工作代理給Number.TryParseInt32

(9)如果隻是從XML對象讀取資料,用隻讀的XPathDocument(System.Xml.XPath)代替XmlDocument (System.Xml),可以提高性能。

//避免

XmlDocument xmld = new XmlDocument();

xmld.LoadXml(sXML);

txtName.Text = xmld.SelectSingleNode( "/packet/child").InnerText;

//推薦

XPathDocument xmldContext = new XPathDocument(new StringReader(oContext.Value));

XPathNavigator xnav = xmldContext.CreateNavigator();

XPathNodeIterator xpNodeIter = xnav.Select("packet/child");

iCount = xpNodeIter.Count;

xpNodeIter = xnav.SelectDescendants(XPathNodeType.Element, false);

while (xpNodeIter.MoveNext())

{

sCurrValues += xpNodeIter.Current.Value + ",";

}

(10)避免在循環體裡聲明變量,應該在循環體外聲明變量,在循環體裡初始化。C#程式開發要遵循的一個基本原則就是避免不必要的對象建立。

//避免

for (int i = 0; i < 10; i++)

        {

            SomeClass objSC = new SomeClass();

        }

//推薦

SomeClass objSC = null;

        for (int i = 0; i < 10; i++)

        {

            objSC = new SomeClass();

        }

(11)捕獲指定的異常,不要使用通用的System.Exception

//避免

try

{

<some logic>

}

catch(Exception exc)

{

<Error handling>

}

//推薦

try

{

<some logic>

}

catch(System.NullReferenceException exc)

{

<Error handling>

}

catch(System.ArgumentOutOfRangeException exc)

{

<Error handling>

}

catch(System.InvalidCastException exc)

{

<Error handling>

}

(12)使用try...catch...finally時, 要在finally裡釋放占用的資源如連接配接,檔案流等。不然在catch到錯誤後占用的資源不能釋放。

try

        { }

        catch

        { }

        finally

        {

            connection.close();

        }

(13)避免使用遞歸調用和嵌套循環,使用他們會嚴重影響性能,在不得不用的時候才使用。

(14)禁用VB和Jscript動态資料類型。應當始終顯示地申明變量資料類型,這能夠節約程式的執行時間以往,開發人員喜歡使用VBScript和JavaScript的原因之一就是它們所謂無類型的性質變量不需要顯式類型聲明,并能夠簡單地通過使用來建立它們當從一個類型到另一個類型進行配置設定時,轉換将自動執行不過,這種便利會大大損害應用程式的性能。如:為了獲得最佳的性能,當聲明 JavaScript  .NET 變量時,請為其配置設定一個類型例如,var A : String

(15)使用緩存。ASP.NET中常用的緩存方式有:

1)頁面緩存(對整個頁面進行緩存)

<%@ OutputCache VaryByParam="classid;page" Duration="3600" %>

2)片斷緩存(對頁面的某一部分,如某個Control進行緩存)

<%@ OutputCache Duration="60" VaryByParam=TextBox1;TextBox2 %>

3)資料緩存

Cache [關鍵字] = 關鍵字的取值;然後通過下面的方法來通路這個對象:

string mKeyValue = string.Empty;

if (Cache[關鍵字] != null)

{

    mKeyValue = Cache[關鍵字];

}

注意Page.Cache和HttpContext.Current.Cache差別:它們指的同一個對象,在Page裡,用Page.Cache,如果在global.asax或自己的類裡用: HttpContext.Current.Cache,在有些事件中,由于其沒有HttpContext,就用HttpRuntime.Cache。

使用Output Cache緩存資料。

緩存資料的規則:第一,資料可能會被頻繁的被使用,這種資料可以緩存。第二,資料的通路頻率非常高,或者一個資料的通路頻率不高,但是它的生存周期很長。第三,應該估計緩存集的大小,把緩存集的大小限制在一定的範圍内,避免出現記憶體溢出的錯誤。

緩存機制:首先是緩存實作了最近使用原則(a least-recently-used algorithm),當緩存少的時候,它會自動的強制清除那些無用的緩存。其次 條件依賴強制清除原則(expiration dependencies),條件可以是時間,關鍵字和檔案以時間作為條件是最常用的在asp2.0中增加一個更強的條件,就是資料庫條件當資料庫中的資料發生變化時,就會強制清除緩存。

使用 ASP.NET緩存機制有兩點需要注意:首先,不要緩存太多項緩存每個項均有開銷,特别是在記憶體使用方面不要緩存容易重新計算和很少使用的項;其次,給緩存的項配置設定的有效期不要太短,很快到期的項會導緻緩存中不必要的周轉,并且經常導緻更多的代碼清除和垃圾回收工作。

應用總結:

應該:

應該緩存那些經常被通路同時變化頻率不大的資料

應該緩存整個應用程式都要使用的設定或對象,但這些設定和對象必須在其生存期内不會變化

不應該:

不要緩存個人資訊如果緩存個人資訊,其他人很容易取得這些資訊

不要緩存包含基于時間值的頁面,否則浏覽者将無法了解為何時間總是滞後

不要緩存使用者随時都會修改的對象,如類似購物車的相關資訊

3、頁面以及其他非代碼元素

(1)不使用不必要的伺服器控件(Server Control)。大量的伺服器端控件友善了程式開發,但也可能帶來性能的損失,因為使用者每操作一次伺服器端控件,就産生一次與伺服器端的往返過程。是以,盡量選擇html控件,能在用戶端實作的功能就在用戶端實作,減少伺服器的壓力。

(2)不使用不必要的ViewState。設定控件的 EnableViewState屬性設定為 false,禁用ViewState 。

還可以使用 @ Page 指令禁用整個頁的視圖狀态 <%@ Page EnableViewState="false" %>

(3)避免到伺服器的不必要的往返過程。

  protected void Page_Load(object sender, EventArgs e)

{

     // 代碼

       if (!Page.IsPostBack)

        {

         // 代碼

        }

}

(4)當不使用會話狀态時禁用它,并且程式開發中盡量少用Session。并不是所有的應用程式或頁都需要針對于具體使用者的會話狀态,您應該對任何不需要會話狀态的應用程式或頁禁用會話狀态。若要禁用頁的會話狀态,設定 @ Page 指令中的 EnableSessionState屬性為 false

若要禁用應用程式的會話狀态,設定應用程式 Web.config 檔案的 sessionState配置節中将 mode屬性為off

(5)合理使用DataGrid、GridView控件。

(6)對資料進行分頁。

(7)不要禁用 Web 窗體頁的緩沖。除非有特殊的原因要關閉緩沖,否則使其保持打開,禁用 Web 窗體頁的緩沖會導緻大量的性能開銷。

(8) 優化頁面結構,減少JavaScript和CSS加載數量。

(9) 頁面的圖檔根據需要進行必要的壓縮處理。

Web伺服器優化

1、伺服器優化

盡可能高的系統硬體配置,主要是記憶體和CPU的選擇。

2、部署方案的優化

根據項目的實際情況,可以建立web service ,将商業邏輯或資料通路放置其中,然後部署在另一台伺服器。