一、 ExecuteNonQuery和ExecuteScalar
對數據的更新不需要返回結果集,建議使用ExecuteNonQuery。由於不返回結果集可省掉網絡數據傳輸。它僅僅返回受影響的行數。如果只需更新數據用ExecuteNonQuery性能的開銷比較小。
ExecuteScalar它只返回結果集中第一行的第一列。使用 ExecuteScalar 方法從數據庫中檢索單個值(例如id號)。與使用 ExecuteReader 方法, 返回的數據執行生成單個值所需的操作相比,此操作需要的代碼較少。
只需更新數據用ExecuteNonQuery.單個值的查詢使用ExecuteScalar 。
二、 SqlDataRead和Dataset的選擇
Sqldataread優點:讀取數據非常快。如果對返回的數據不需做大量處理的情況下,建議使用SqlDataReader,其性能要比datset好很多。缺點:直到數據讀完才可close掉於數據庫的連接 。(SqlDataReader 讀數據是快速向前的。SqlDataReader 類提供了一種讀取從 SQL Server 數據庫檢索的只進數據流的方法。它使用 SQL Server 的本機網絡數據傳輸格式從數據庫連接直接讀取數據。DataReader需及時顯式的close。可及時的釋放對數據的連接。)
Dataset是把數據讀出,緩存在內存中。缺點:對內存的占用較高。如果對返回的數據需做大量的處理用Dataset比較好些可以減少對數據庫的連接操作。優點:只需連接一次就可close於數據庫的連接。
一般情況下,讀取大量數據,對返回數據不做大量處理用SqlDataReader.對返回數據大量處理用datset比較合適.對SqlDataReader和Dataset的選擇取決於程序功能的實現。
數據綁定的選擇
三、 數據的綁定DataBinder
一般的綁定方法<%# DataBinder.Eval(Container.DataItem, "字段名") %> 用DataBinder.eval 綁定不必關心數據來源(Dataread或dataset)。不必關心數據的類型eval會把這個數據對象轉換為一個字符串。在底層綁定做了很多工作,使用了反射性能。正因為使用方便了,但卻影響了數據性能。
來看下<%# DataBinder.Eval(Container.DataItem, "字段名") %>。當於dataset綁定時,DataItem其實式一個DataRowView(如果綁定的是一個數據讀取器(dataread)它就是一個IdataRecord。)因此直接轉換成DataRowView的話,將會給性能帶來很大提升。.
<%# ctype(Container.DataItem,DataRowView).Row("字段名") %>
對數據的綁定建議使用<%# ctype(Container.DataItem,DataRowView).Row("字段名") %>。數據量大的時候可提高幾百倍的速度。使用時注意2方面:1.需在頁面添加<%@ Import namespace="System.Data"%>.2.注意字段名的大小寫(要特別注意)。如果和查詢的不一致,在某些情況下會導致比<%# DataBinder.Eval(Container.DataItem, "字段名") %>還要慢。如果想進一步提高速度,可采用<%# ctype(Container.DataItem,DataRowView).Row(0) %>的方法。不過其可讀性不高。
以上的是vb.net的寫法。在c#中:
<@% ((DataRowView)Container.DataItem)["字段名"] %>
一、 應用Ado.net的一些思考原則
-
選擇適當的事務類型
-
使用存儲過程
-
根據數據使用的方式來設計數據訪問層
-
必要時申請,盡早釋放
-
緩存數據,避免不必要的操作
-
使用服務帳戶進行連接
-
減少往返
-
僅返回需要的數據
-
關閉可關閉的資源
二、 Connection
數據庫連接是一種共享資源,並且打開和關閉的開銷較大。Ado.net默認啟用了連接池機制,關閉連接不會真的關閉物理連接,而只是把連接放回到連接池中。因為池中共享的連接資源始終是有限的,如果在使用連接后不盡快關閉連接,
那么就有可能導致申請連接的線程被阻塞住,影響整個系統的性能表現。
1、在方法中打開和關閉連接
這個原則有幾層含義:
1)主要目的是為了做到必要時申請和盡早釋放
2)不要在類的構造函數中打開連接、在析構函數中釋放連接。因為這將依賴於垃圾回收,而垃圾回收只受內存影響,
回收時機不定
3)不要在方法之間傳遞連接,這往往導致連接保持打開的時間過長
這里強調一下在方法之間傳遞連接的危害:曾經在壓力測試中遇到過一個測試案例,當增大用戶數的時候,這個案例要比別
的案例早很久就用掉連接池中的所有連接。經分析,就是因為A方法把一個打開的連接傳遞到了B方法,而B方法又調用了一個
自行打開和關閉連接的C方法。在A方法的整個運行期間,它至少需要占用兩條連接才能夠成功工作,並且其中的一條連接占用時間還特別長,所以造成連接池資源緊張,影響了整個系統的可伸縮性!
2、顯式關閉連接
Connection對象本身在垃圾回收時可以被關閉,而依賴垃圾回收是很不好的策略。推薦使用using語句顯式關閉連接,如下例:
using (SqlConnection conn =new SqlConnection(connString)) { conn.Open(); } // Dispose is automatically called on the conn variable here
3、確保連接池啟用
Ado.net是為每個不同的連接串建立連接池,因此應該確保連接串不會出現與具體用戶相關的信息。另外,要注意連接串是
大小寫敏感的。
4、不要緩存連接
例如,把連接緩存到Session或Application中。在啟用連接池的情況下,這種做法沒有任何意義。
三、Command
1、 使用ExecuteScalar和ExecuteNonQuery
如果想返回像Count(*)、Sum(Price)或Avg(Quantity)那樣的單值,可以使用ExecuteScalar方法。ExecuteScalar返回第一行第一列的值,將結果集作為標量值返回。因為單獨一步就能完成,所以ExecuteScalar不僅簡化了代碼,還提高了性能。
使用不返回行的SQL語句時,例如修改數據(INSERT、UPDATE或DELETE)或僅返回輸出參數或返回值,請使用ExecuteNonQuery。這避免了用於創建空DataReader的任何不必要處理。
2、使用Prepare
當需要重復執行同一SQL語句多次,可考慮使用Prepare方法提升效率。需要注意的是,如果只是執行一次或兩次,則完全沒有必要。例如:
cmd.CommandText ="insert into Table1 ( Col1, Col2 ) values ( @val1, @val2 )"; cmd.Parameters.Add( "@val1", SqlDbType.Int, 4, "Col1" ); cms.Parameters.Add( "@val2", SqlDbType.NChar, 50, "Col2"); cmd.Parameters[0].Value =1; cmd.Parameters[1].Value ="XXX"; cmd.Prepare(); cmd.ExecuteNonQuery(); cmd.Parameters[0].Value =2; cmd.Parameters[1].Value ="YYY"; cmd.ExecuteNonQuery(); cmd.Parameters[0].Value =3; cmd.Parameters[1].Value ="ZZZ"; cmd.ExecuteNonQuery();
3、使用綁定變量
SQL語句需要先被編譯成執行計划,然后再執行。如果使用綁定變量的方式,那么這個執行計划就可以被后續執行的SQL語句所復用。而如果直接把參數合並到了SQL語句中,由於參數值千變萬化,執行計划就難以被復用了。例如上面Prepare一節給出的示例,如果把參數值直接寫到insert語句中,那么上面的四次調用將需要編譯四次執行計划。
為避免這種情況造成性能損失,要求一律使用綁定變量方式。
四、 DataReader
DataReader最適合於訪問只讀的單向數據集。與DataSet不同,數據集並不全部在內存中,而是隨不斷發出的read請求,一旦發現數據緩沖區中的數據均被讀取,則從數據源傳輸一個數據緩沖區大小的數據塊過來。另外,DataReader保持連接,DataSet則與連接斷開。
1、 顯式關閉DataReader
與連接類似,也需要顯式關閉DataReader。另外,如果與DataReader關聯的Connection僅為DataReader服務的話,可考慮使用Command對象的ExecuteReader(CommandBehavior.CloseConnection)方式。這可以保證當DataReader關閉時,同時自動關閉Connection。
2、用索引號訪問代替名稱索引號訪問屬性
從Row中訪問某列屬性,使用索引號的方式比使用名稱方式有細微提高。如果會被頻繁調用,例如在循環中,那么可考慮此類優化。示例如下:
cmd.CommandText ="select Col1, Col2 from Table1" ; SqlDataReader dr = cmd.ExecuteReader(); int col1 = dr.GetOrdinal("Col1"); int col2 = dr.GetOrdinal("Col2"); while (dr.Read()) { Console.WriteLine( dr[col1] +"_"+ dr[col2]); }
3、使用類型化方法訪問屬性
從Row中訪問某列屬性,用GetString、GetInt32這種顯式指明類型的方法,其效率較通用的GetValue方法有細微提高,因為不需要做類型轉換。
4、使用多數據集
部分場景可以考慮一次返回多數據集來降低網絡交互次數,提升效率。示例如下:
cmd.CommandText ="StoredProcedureName"; // The stored procedure returns multiple result sets.SqlDataReader dr = cmd.ExecuteReader(); while (dr.read()) // read first result set dr.NextResult(); while (dr.read())
五、DataSet
1、 利用索引加快查找行的效率
如果需要反復查找行,建議增加索引。有兩種方式:
1)設置DataTable的PrimaryKey
適用於按PrimaryKey查找行的情況。注意此時應調用DataTable.Rows.Find方法,一般慣用的Select方法不能利用索引。
2)使用DataView
適用於按Non-PrimaryKey查找行的情況。可為DataTable創建一個DataView,並通過SortOrder參數指示建立索引。此后使用Find或FindRows查找行。
一、減少往返行程(Reduce Round Trips)
使用下面的方法可以減少Web服務器和Browser之間的往返行程:
1、為Browser啟用緩存
如果呈現的內容是靜態的或變化周期較長,應啟用Browser緩存,避免發出冗余的http請求。
2、緩沖頁面輸出
如果可能,則盡量緩沖頁面輸出,處理結束后再一次傳送到客戶端,這可以避免頻繁傳遞小塊內容所造成的多次網絡交互。由於這種方式在頁面處理結束之前客戶端無法看到頁面內容,因此如果一個頁面的尺寸較大的話,可考慮使用Response.Flush方法。該方法強制輸出迄今為止在緩沖區中的內容,你應當采用合理的算法控制調用Response.Flush方法的次數。
3、使用Server.Transfer重定向請求
使用Server.Transfer方法重定向請求優於Response.Redirect方法。原因是Response.Redirect會向Broswer回送一個響應頭,在響應頭中指出重定向的URL,之后Brower使用新的URL重新發出請求。而Server.Transfer方法直接是一個簡單的服務端調用,完全沒有這些開銷!
需要注意Server.Transfer有局限性:第一,它會跳過安全檢查;第二,只適用於在同一Web應用內的頁面間跳轉。
二、避免阻塞和長時間的作業
如果需要運行阻塞或長時間運行的操作,可以考慮使用異步調用的機制,以便Web服務器能夠繼續處理其它的請求。
1、使用異步方式調用Web服務和遠程對象
只要有可能就要避免在請求的處理過程中對Web服務和遠程對象的同步調用,因為它占用的是的ASP.NET 線程池中的工作線程,這將直接影響Web服務器響應其它請求的能力。
2、考慮給不需要返回值的Web方法或遠程對象的方法添加OneWay屬性
這種模式能讓Web Server調用之后就立即返回。可根據實際情況決定是否使用這種方法。
3、使用工作隊列
將作業提交到服務器上的工作隊列中。客戶端通過發送請求來輪詢作業的執行結果。
三、 使用緩存
緩存能在很大程度上決定ASP.NET應用的最終性能。Asp.net支持頁面輸出緩存和頁面部分緩存,並提供Cache API,供應用程序緩存自己的數據。是否使用緩存可考慮下面的要點:
1、識別創建與訪問代價較大的數據
2、評估需要緩存數據的易變性
3、評估數據的使用頻次
4、 將要緩存數據中易變數據和不變數據分離,只緩存不變數據
5、選擇合適的緩存機制(除Asp.net Cache外,Application state和Session state也可以作為緩存使用)
四、 系統資源
1、考慮實現資源池以提升性能
2、明確地調用Dispose或Close釋放系統資源
3、不要緩存或長時間占用資源池中的資源
4、盡可能晚的申請,盡可能早的釋放
五、多線程
1、避免在請求處理過程中創建線程
在執行請求的過程中創建線程是一種代價較大的操作,會嚴重影響Web Server的性能。如果后續的操作必須用線程完成,建議通過thread pool來創建/管理線程。
2、避免阻塞處理請求的線程
3、避免異步調用
這和1的情況類似。異步調用會導致創建新的線程,增加服務器的負擔。所以,如果沒有並發的作業要執行,就不要執行異步調用。
4、不要依賴線程數據槽或線程靜態變量
由於執行請求的線程是ASP.NET thread pool中的工作線程,同一個Client的兩次請求不一定由相同的線程來處理。
六、 頁面處理
1、盡量減小Page的尺寸
包括縮短控件的名稱、CSS的class的名稱、去掉無謂空行和空格、禁用不需要的ViewState
2、優化復雜和代價較大的循環
3、合理利用客戶端的計算資源,將一些操作轉移到客戶端進行
4、啟用頁面輸出的緩沖區(Buffer)
如果Buffer的機制被關閉,可以用下面的方法打開。
使用程序打開頁面輸出緩存:
Response.BufferOutput = true; <%@ Page Buffer = "true" %>
使用Web.config或Machine.config配置文件的<pages>節點:
<pages buffer="true" …>
5、利用Page.IsPostBack優化頁面輸出
6、通過分離頁面的不同的內容,來提高緩存效率和減少呈現的時間
七、 ViewState
ViewState是Asp.net為服務端控件在頁面回傳之間跟蹤狀態信息而設計的一種機制。
1、在恰當的時間點初始化控件屬性
ASP.NET的控件在執行構造函數、初始化的期間設置的屬性不會被跟蹤變化;而在初始化階段之后對屬性的修改都會被跟蹤,並最終記錄到IE頁面的__VIEWSTATE之中。所以,選擇合理的初始化控件屬性的執行點,能有效的減小頁面尺寸。
2、謹慎選擇放到ViewState中的內容
放到ViewState中的內容會被序列化/反序列化,Asp.net為String、Integer、Boolean等基本類型的序列化做了優化,如果Array、ArrayList、HashTable存儲的是基本類型效率也較高,但其它類型則需要提供類型轉換器(Type Converter),否則將使用代價昂貴的二進制序列化程序。
3、 關閉ViewState
如果不需要跟蹤頁面狀態,例如頁面不會 回傳(PostBack)、不需要處理服務端控件事件或者每次頁面刷新時都會重新計算控件內容,那么就不需要用ViewState來記錄頁面狀態了。可以對特定的WebControl設置EnableViewState屬性,也可以在頁面一級設置:
<%@ Page EnableViewState="false" %>
歡迎關注我的公眾號(同步更新文章):DoNet技術分享平台
閱讀原文