ADO.Net SQLCommand.ExecuteReader()减慢或挂起

Cor*_*ndt 7 .net c# sql sql-server ado.net

环境:

应用程序(用C#编写的.Net 4)最多有10个线程,每个线程都在自己的AppDomain中运行.每个线程都使用ADO.Net DataReader从SQL-Server 2008上获取存储过程的结果.另外一个线程可以使用ADO.Net来执行写操作(Bulk Insert).一切都在本地机器上运行.

问题#1:

偶尔(大约每30次运行)线程的执行会急剧减慢.当DataReader获取存储过程结果时会发生这种情况 - SqlCommand.ExecuteReader().通常读操作在10秒内执行.当它减速时,它会在10-20分钟内执行.SQLProfiler显示正在查询数据,但速度非常慢.

缓和的调用堆栈(请注意,没有例外):

at SNIReadSync(SNI_Conn* , SNI_Packet** , Int32 )
   at SNINativeMethodWrapper.SNIReadSync(SafeHandle pConn, IntPtr& packet, Int32 timeout)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   at System.Data.SqlClient.TdsParserStateObject.ReadByteArray(Byte[] buff, Int32 offset, Int32 len)
   at System.Data.SqlClient.TdsParserStateObject.ReadString(Int32 length)
   at System.Data.SqlClient.TdsParser.ReadSqlStringValue(SqlBuffer value, Byte type, Int32 length, Encoding encoding, Boolean isPlp, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.ReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, Int32 length, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ReadColumnData()
   at System.Data.SqlClient.SqlDataReader.ReadColumnHeader(Int32 i)
   at System.Data.SqlClient.SqlDataReader.ReadColumn(Int32 i, Boolean setTimeout)
   at System.Data.SqlClient.SqlDataReader.GetValueInternal(Int32 i)
   at System.Data.SqlClient.SqlDataReader.GetValue(Int32 i)
   at System.Data.SqlClient.SqlDataReader.get_Item(String name)
   at ****.Core.TableDataImporter.ImportDataFromExcel(Int32 tableId, ExcelEntityLocation location, Boolean& updateResult) in …
Run Code Online (Sandbox Code Playgroud)

问题#2:

而不是减慢线程可以挂起.

调用堆栈:

at SNIReadSync(SNI_Conn* , SNI_Packet** , Int32 )
   at SNINativeMethodWrapper.SNIReadSync(SafeHandle pConn, IntPtr& packet, Int32 timeout)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   at System.Data.SqlClient.TdsParserStateObject.ReadByte()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader()
Run Code Online (Sandbox Code Playgroud)

使用后台线程中的调试工具获取了调用堆栈.没有例外,无论是减速还是挂断.

SNIReadSync是一种在网络级别工作的机制,适用于通过网络传输数据包.我们在本地机器上重现了这个问题,从等式中消除了网络问题.

我们正在寻找这种减速/挂机的任何输入和解决方案或解决方法.目前我们计划检测减速并重新运行该操作.提前致谢.

我正在按要求添加方法的简化代码:

  public void ImportDataFromExcel()
    {            
        try
        {                
            var _?onnectionBuilk = ... ; // singleton connection (at the app level)
            var spName = ... ; // stored procedure name

        var ?onnectionToRead = new SqlConnection(connectionStirng);
        ?onnectionToRead.Open();

        var sqlCommand = new SqlCommand(spName);
        sqlCommand.CommandType = CommandType.StoredProcedure; 
        sqlCommand.Parameters.Add(param1Name, SqlDbType.Int).Value = ...;
        sqlCommand.Parameters.Add(param2Name, SqlDbType.Int).Value = ...;
        sqlCommand.Parameters.Add(param2Name, SqlDbType.Int).Value = ...;

        sqlCommand.Connection = ?onnectionToRead;            
        sqlCommand.CommandTimeout = timeout; // 120 sec

        using (var dataReader = sqlCommand.ExecuteReader())
        {
                dataReader.Read();
            .....
            int pos1 = dataReader.GetOrdinal(columnName1);
            int pos2 = dataReader.GetOrdinal(columnName2);
            int pos3 = dataReader.GetOrdinal(columnName3);
            int pos4 = dataReader.GetOrdinal(columnName4);
                .....                    

            // reading data from sqldatareader
            int val1 = dataReader.GetInt32(pos1);
            int val2 = dataReader.GetInt32(pos2);
            int val3 = dataReader.GetInt32(pos3);
            var val4 = dataReader.GetDateTime(pos4);
            .....

            // append read data into bulkTable
            bulkTable.AddCellValue(val1, val2, val3, val4);  // bulkTable wraps DataTable, and appends DataRow inside. 

            if(bulkTable.DataTable.Rows > MaxRowsCount)
            {
                using (var bulkCopy = new SqlBulkCopy(_?onnectionBuilk))
                {
                    bulkCopy.DestinationTableName = _fullTableName;
                    bulkCopy.WriteToServer(bulkTable.DataTable);
                }

                var sqlCommandTransfer = new SqlCommand(spName);
                sqlCommandTransfer.CommandType = CommandType.StoredProcedure; 
                sqlCommandTransfer.Parameters.Add(param1Name, SqlDbType.Int).Value = ...;
                sqlCommandTransfer.Connection = _?onnectionBuilk;
                ....
                sqlCommandTransfer.ExecuteNonQuery(); // transfering data from temp bulk table into original table
            }
        }
    }
    finally
    {
        bulkTable.Dispose();
        ?onnectionToRead.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*eGO 1

因为代码在一段时间内可以完美运行,所以我们可以将其范围缩小到:

  • 数据库锁定/阻止您的进程和其他一些进程。
  • 仅从您的进程锁定/阻止数据库。
  • 网络连接。
  • 数据状态。
  • 服务器上的磁盘空间或其他一些看似不相关的问题。

我想说这可能是数据库锁/块问题。但你确实需要确定这一点。去做这个:

  • 确保有数据库写入的磁盘空间 - 包括数据库日志和任何其他日志记录。
  • 确保没有其他进程正在使用该数据库。
  • 最好也使用本地数据库来消除网络问题。
  • 您正在使用 .Net 4 - 因此,如果您正在使用任务,那么让它们与重载同步运行将非常容易。执行此操作并查看问题是否仍然存在。

执行上述所有操作应该可以消除问题 - 您可以从那里开始进一步缩小范围。