1. 程式人生 > >select * from table 時間長

select * from table 時間長

    優化中發現一個儲存過程執行20秒通過profiler 抓取發現時間主要消耗在一個select * from 表,那麼問題來了select幾萬資料竟然花了將近20秒?

    問題排查清了程式前端使用了datareader獲取資料,那麼datareader對資料庫有什麼影響呢?下面來做個實驗測試一下。首先我們建立測試表並插入200條資料。

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毫秒?竟然不像想象中的時間會長? 這是為什麼呢?

    測試繼續! 我們加大資料,繼續新增200條。

insert into [table_2]
select 1,getdate(),newid()
go 200

    測試效果一樣0毫秒。

    繼續增加200 條 目前資料600條

    

    看一下效果:

    

    效果神奇的出現了 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接收結果集使用要注意接收結果後要避免在迴圈中有大量的耗時處理。