ADO.NET损害性能:如何防止DataReader耗费这么长时间?

时间:2014-08-08 14:48:20

标签: c# .net ado.net

Oracle存储过程在数据库中运行并返回一些数据行,耗时30秒。 现在,调用此过程并填充DataAdapter然后填充DataSet需要在C#.NET应用程序中使用1m40。

测试时发现,在调用存储过程后,使用DataReader并使用Read()函数读取数据,再次需要1m40s aprox的总时间。

任何想法可能导致这些瓶颈以及如何摆脱它们? 提前致谢!

编辑:添加了代码

        OracleConnection oracleConnection = Connect();
        OracleCommand oracleCommand = CreateCommand(2);
        OracleDataReader oracleDataReader = null;

        if (oracleConnection != null)
        {
            try
            {
                if (oracleConnection.State == ConnectionState.Open)
                {
                    oracleCommand.Connection = oracleConnection;
                    oracleDataReader = oracleCommand.ExecuteReader();
                    DateTime dtstart = DateTime.Now;
                    if (oracleDataReader.HasRows)
                    {
                        while (oracleDataReader.Read())
                        {
                             /* big Bottleneck here ... */

                            // Parse the fields
                        }
                    }
                    DateTime dtEnd = DateTime.Now;
                    TimeSpan ts = new TimeSpan(dtEnd.Ticks - dtstart.Ticks);
                    lblDuration2.Text = "Duration: " + ts.ToString();
                    Disconnect(oracleConnection);
                }

1 个答案:

答案 0 :(得分:1)

这可能会有所帮助,尽管缺乏有关您如何使用阅读器的信息。

using (var cnx = Connect()) 
    using (var cmd = CreateCommand(2)) {
         try {
             if (cnx.State == ConnectionState.Close) cnx.Open()

             // The following line allows for more time to be allowed to
             // the command execution. The smaller the amount, the sooner the
             // command times out. So be sure to let enough room for the 
             // command to execute successfuly
             cmd.CommandTimeout = 600; 

             // The below-specified CommandBehavior allows for a sequential
             // access against the underlying database. This means rows are 
             // streamed through your reader instance and meanwhile the 
             // program reads from the reader, the reader continues to read 
             // from the database instead of waiting until the full result 
             // set is returned by the database to continue working on the 
             // information data.
             using (var reader = cmd.ExecuteReader(
                                          CommandBehavior.SequentialAccess)) {
                 if (reader.HasRows)
                     while (reader.Read()) {
                         // Perhaps bottleneck will disappear here...
                         // Without proper code usage of your reader
                         // no one can help.
                     }
             }
         } catch(OracleException ex) {
             // Log exception or whatever, 
             // otherwise might be best to let go and rethrow
         } finally {
             if (cnx.State == ConnectionState.Open) cnx.Close();
         }
    }

有关命令行为的更多详细信息:Command Behavior Enumeration

直接来自MSDN:

顺序访问

  

DataReader 提供一种方法来处理包含具有大二进制值的列的行。 SequentialAccess 不是加载整行,而是使 DataReader 以数据流的形式加载数据。然后,您可以使用 GetBytes GetChars 方法指定启动读取操作的字节位置,以及返回数据的有限缓冲区大小。

     

指定 SequentialAccess 时,您需要按照返回的顺序读取列,但不需要读取每列。一旦您读取了返回的数据流中的某个位置,就无法再从 DataReader 中读取该位置或之前的数据。使用OleDbDataReader时,您可以重读当前列值,直到读过它为止。使用SqlDataReader时,您只能读取一次列值。

至于增加CommandTimeout属性,请看一下这篇文章:

Increasing the Command Timeout for SQL command

当您希望命令花费一定的时间时,需要更长的超时时间并允许命令在超时之前返回。发生超时时,需要几秒钟才能从中恢复。所有这一切都可以避免。您可能希望测量超时所需的时间,并将其指定为尽可能接近实际命令超时要求,因为较长的超时可能会导致一些其他潜在问题,这些问题在超时时间过长时无法检测到。发生命令超时时,请问自己如何处理较小的结果集,或者如何改进查询以加快运行速度。