优化中发现一个存储过程执行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接收结果集使用要注意接收结果后要避免在循环中有大量的耗时处理。