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

环境:

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

问题#1:

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

减速的Callstack(请注意,没有例外):

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 …

问题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()

在后台线程中使用调试工具获取调用堆栈。 没有例外发生,无论是放缓还是挂断。

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();
    }
}

我们一直在试图调试一个类似的问题几个月,并最终在今天跟踪它...

我们有一个查询被隐藏到缓存中(没有calliong ToList / ToArray /等等)。 该查询实际上与已经清理的连接有关,并且我们得到了来自[code] ReadSni [/ code](完整堆栈包括在下面)的100%CPU阻塞。

我怀疑缓存代码是在查询被改为使用Linq之前编写的(并且用于返回一个List,但仍然转换为IEumberable),所以当有人将数据访问设置为“懒惰”时引入了它。

我无法解释为什么它只在生产中每隔几天发生一次; 要么缓存没有被大量使用,要么连接必须处于某种状态才能以这种方式失败。

[代码] OS线程Id:0x20b8(27)子SP IP调用站点16edd0fc 6184267e System.Data.SqlClient.TdsParserStateObject.ReadSni(System.Data.Common.DbAsyncResult,System.Data.SqlClient.TdsParserStateObject)16edd134 61842624 System.Data。 SqlClient.TdsParserStateObject.ReadNetworkPacket()16edd144 618446af System.Data.SqlClient.TdsParserStateObject.ReadBuffer()16edd150 61c583d0 System.Data.SqlClient.TdsParserStateObject.CleanWire()16edd15c 61d1beb9 System.Data.SqlClient.TdsParser.Deactivate(Boolean)16edd174 6184995f System .Data.SqlClient.SqlInternalConnectionTds.InternalDeactivate()16edd180 61849640 System.Data.SqlClient.SqlInternalConnection.Deactivate()16edd1b0 61849587 System.Data.ProviderBase.DbConnectionInternal.DeactivateConnection()16edd1e4 61849405 System.Data.ProviderBase.DbConnectionPool.DeactivateObject(System。 Data.ProviderBase.DbConnectionInternal)16edd224 61849384 System.Data.ProviderBase.DbConnectionPool.PutObject(System.Data.ProviderBase.DbConnectionInternal,System.Obje cd)16edd26c 6184920c System.Data.ProviderBase.DbConnectionInternal.CloseConnection(System.Data.Common.DbConnection,System.Data.ProviderBase.DbConnectionFactory)16edd2ac 618490f7 System.Data.SqlClient.SqlInternalConnection.CloseConnection(System.Data.Common.DbConnection, System.Data.ProviderBase.DbConnectionFactory)16edd2c4 618393bf System.Data.SqlClient.SqlConnection.Close()16edd304 11238f0a NHibernate.Connection.ConnectionProvider.CloseConnection(System.Data.IDbConnection)16edd340 11238eae NHibernate.Connection.DriverConnectionProvider.CloseConnection(System.Data .IDbConnection)16edd34c 11aceb42 NHibernate.AdoNet.ConnectionManager.CloseConnection()16edd358 11aceb02 NHibernate.AdoNet.ConnectionManager.AggressiveRelease()16edd364 11acf783 NHibernate.AdoNet.ConnectionManager.AfterTransaction()16edd370 11acf6d1 NHibernate.Impl.SessionImpl.AfterTransactionCompletion(Boolean,NHibernate。 ITransaction)16edd3ec 11acf5de NHibernate.AdoNet.ConnectionManager.AfterNonTransactionalQuery(B (NHibernate.IQueryExpression,NHibernate.Engine.QueryParameters,System.Collections.IList)16ede51c 13031071 NHibernate.Impl.AbstractSessionImpl.List NHibernate.IQueryExpression,NHibernate.Engine.QueryParameters)16ede538 13030b68 NHibernate.Impl.ExpressionQueryImpl.List()16ede568 13030a47 NHibernate.Linq.DefaultQueryProvider.ExecuteQuery(NHibernate.Linq.NhLinqExpression,NHibernate.IQuery,NHibernate.Linq.NhLinqExpression)16ede59c 11d4c163 NHibernate .Linq.DefaultQueryProvider.Execute(System.Linq.Expressions.Expression)16ede5b0 11d4c108 NHibernate.Linq.DefaultQueryProvider.Execute [System._Canon,mscorlib] 16ede5c4 11d4c0a6 Remotion.Linq.QueryableBase 1[[System.__Canon, mscorlib]].GetEnumerator() 16ede5d4 61022108 System.Linq.Enumerable+WhereEnumerableIterator 1 [[System._Canon,mscorlib]]。MoveNext() *警告:无法验证System.Co的校验和 re.ni.dll *错误:模块加载完成但符号无法加载System.Core.ni.dll

16ede5e4 610166ea System.Linq.Buffer 1[[System.__Canon, mscorlib]]..ctor(System.Collections.Generic.IEnumerable 1)16ede620 6122e171 System.Linq.OrderedEnumerable 1+<GetEnumerator>d__0[[System.__Canon, mscorlib]].MoveNext() 16ede63c 79b39758 System.Collections.Generic.List 1 [[System .__ Canon,mscorlib]] .. ctor(System.Collections.Generic.IEnumerable`1) *警告:无法验证mscorlib.ni的校验和.dll *错误:模块加载完成但符号无法加载mscorlib.ni.dll

16ede66c 61021acf System.Linq.Enumerable.ToList“> [System .__ Canon,mscorlib] [/ code]


由于代码完美工作了一段时间,我们可以将其缩小到:

  • 数据库锁定/阻止您的进程和其他人。
  • 数据库锁定/阻止仅来自进程。
  • 网络连接。
  • 数据的状态。
  • 磁盘空间或服务器上一些其他看似不相关的问题。
  • 我会说这可能是一个数据库锁/块问题。 但你确实需要确定这一点。 去做这个:

  • 确保数据库正在写入的磁盘空间包括数据库日志和任何其他日志记录。
  • 确保没有其他进程正在使用数据库。
  • 最好使用本地数据库以消除网络问题。
  • 您正在使用.Net 4 - 因此,如果您使用任务,那么使它们与重载同步运行将非常容易。 做到这一点,看看问题是否仍然存在。
  • 做所有上述事情应该可以消除这些问题 - 并且您可以从那里开始缩小范围。


    我遇到了同样的问题,经过多次测试后我解决了我的具体问题,但我认为它可能有用。

    在我的系统中,我在SQLServer 2008上有一个复杂的OLAP引擎。多年来,操作随着数量和数量的增长而增加,随机地发生在后面提到的错误......提升操作错误消失了。 我仔细检查了所有操作OLAP的代码,并且所有事务,连接和阅读器对象都得到了完美处理。

    当我是WHILE循环的一部分来查询SQLServer时,生成错误(但并非总是)的关键点,重复的querys轰炸在DB中产生错误

    Ho ristrutturato le molte piccole querys in una querypiùgrandi(in termini di resultset)ed ho operato delle operazioni attraverso LINQ,il risultatoèstato soprendente。 Perdormance di 10 volte migliorate e 0 errori。

    我可以给出的建议是好好分析代码的关键部分,即使非常小心如何使用Connections,DataReader和Transactions。

    链接地址: http://www.djcxy.com/p/62817.html

    上一篇: ADO.Net SQLCommand.ExecuteReader() slows down or hangs

    下一篇: Bean and EBean conception Android Annotations