select * from table 时间长_MySQL

WBOY
WBOYオリジナル
2016-05-30 17:10:001239ブラウズ

优化中发现一个存储过程执行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 )

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

select * from table 时间长_MySQL

执行时间0毫秒?竟然不像想象中的时间会长? 这是为什么呢?

 

测试继续! 我们加大数据,继续添加200条。

 

insert into [table_2]

select 1,getdate(),newid()

go 200

 

测试效果一样0毫秒。

 

继续增加200 条 目前数据600条

select * from table 时间长_MySQL

看一下效果:

select * from table 时间长_MySQL

效果神奇的出现了 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%'

select * from table 时间长_MySQL

没错就是他 传说中的ASYNE_NETWORK_IO 

 

疑问:按照之前的理解datareader 一次只是读取一条记录...那么为什么我400条记录的时候就不会出现等待呢?

 

想到了发送的网络包大小,mssql默认为4096

select * from table 时间长_MySQL

那么我们继续用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

 

再次测试执行观察效果

select * from table 时间长_MySQL

好吧我猜对了....

 

后续用dataset接收返回结果就不会出现上述问题,所以如网上所说datareader接收结果集使用要注意接收结果后要避免在循环中有大量的耗时处理。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。