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]
由于代码完美工作了一段时间,我们可以将其缩小到:
我会说这可能是一个数据库锁/块问题。 但你确实需要确定这一点。 去做这个:
做所有上述事情应该可以消除这些问题 - 并且您可以从那里开始缩小范围。
我遇到了同样的问题,经过多次测试后我解决了我的具体问题,但我认为它可能有用。
在我的系统中,我在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