優化中發現一個存儲過程執行20秒經過profiler 抓取發現時間主要消耗在一個select * from 表,那麼問題來了select幾萬數據居然花了將近20秒?前端
問題排查清了程序前端使用了datareader獲取數據,那麼datareader對數據庫有什麼影響呢?下面來作個實驗測試一下。首先咱們建立測試表並插入200條數據。sql
1 CREATE TABLE [dbo].[table_2]( 2 [a] [int] NULL, 3 [b] [datetime] NULL, 4 [c] [uniqueidentifier] NOT NULL 5 ) 6 7 insert into [table_2] 8 select 1,getdate(),newid() 9 go 200
編寫一段簡單的C#小程序,使用datareader讀取一個select * from table 在datareader的循環中咱們設置線程等待50毫秒。System.Threading.Thread.Sleep(50);數據庫
protected void Button1_Click(object sender, EventArgs e) { //SqlCommand sqlCmd = new SqlCommand("p_datareader_test", con); //sqlCmd.Connection = con; //sqlCmd.CommandType = CommandType.StoredProcedure;//設置調用的類型爲存儲過程 //SqlParameter sqlParme = new SqlParameter(); //sqlParme = sqlCmd.Parameters.Add("@p", SqlDbType.Int); //sqlParme.Direction = ParameterDirection.Input; //sqlParme.Value = 0; //SqlDataReader reader = sqlCmd.ExecuteReader(); SqlConnection con = new SqlConnection(); con.ConnectionString = "server=vpc-new3;database=replication;uid=sa;pwd=sa_123456"; DataTable dt = new DataTable(); //SQL直接查詢測試------------------------------- con.Open(); SqlCommand com = new SqlCommand(); com.Connection = con; com.CommandType = CommandType.Text; com.CommandText = "select * from table_2"; SqlDataReader reader = com.ExecuteReader(); DataRow dtr = dt.NewRow () ; try { DataTable objDataTable = new DataTable(); int intFieldCount = reader.FieldCount; for (int intCounter = 0; intCounter < intFieldCount; ++intCounter) { objDataTable.Columns.Add(reader.GetName(intCounter), reader.GetFieldType(intCounter)); } objDataTable.BeginLoadData(); object[] objValues = new object[intFieldCount]; while (reader.Read()) { //系統等待 System.Threading.Thread.Sleep(50); reader.GetValues(objValues); objDataTable.LoadDataRow(objValues, true); } reader.Close(); objDataTable.EndLoadData(); GV .DataSource = objDataTable; GV.DataBind(); } catch (Exception ex) { throw new Exception("轉換出錯!", ex); } reader.Close(); con.Close(); }
下面來測試執行一下:小程序
點擊按鈕出發btnclick網絡
執行時間0毫秒?居然不像想象中的時間會長? 這是爲何呢?ide
測試繼續! 咱們加大數據,繼續添加200條。測試
insert into [table_2] select 1,getdate(),newid() go 200
測試效果同樣0毫秒。大數據
繼續增長200 條 目前數據600條優化
看一下效果:ui
效果神奇的出現了 600條記錄的查詢須要12秒!!
咱們再一次執行觀察一下居然12秒才執行完確定會有等待!
select wait_type,brt.text from sys.dm_exec_requests br OUTER APPLY sys.dm_exec_sql_text(br.sql_handle) AS brt where brt.text like '%table_2%'
沒錯就是他 傳說中的ASYNE_NETWORK_IO
疑問:按照以前的理解datareader 一次只是讀取一條記錄...那麼爲何我400條記錄的時候就不會出現等待呢?
想到了發送的網絡包大小,mssql默認爲4096
那麼咱們繼續用200條數據測試此次加大表的長度
drop table table_2 CREATE TABLE [dbo].[table_2]( [a] [int] NULL, [b] [datetime] NULL, [c] [uniqueidentifier] NOT NULL, d char(4000) ) insert into [table_2] select 1,getdate(),newid(),'test' go 200
再次測試執行觀察效果
好吧我猜對了....
後續用dataset接收返回結果就不會出現上述問題,因此如網上所說datareader接收結果集使用要注意接收結果後要避免在循環中有大量的耗時處理。