我有一个使用VS 2010(.NET 4.0)开发的多线程Windows服务,它可以有几个到几十个线程,每个线程通过Internet从慢速服务器检索数据,然后使用本地数据库记录此数据(因此进程受Internet限制,而不是LAN或CPU绑定)。
有一些规律性,我从几个线程同时得到以下错误的洪水/乱舞/爆发:
System.Data.SqlClient.SqlException(0x80131904):超时已过期。操作完成之前经过的超时时间或服务器没有响应。
此错误的调用堆栈通常为:
在System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
在System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
在System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection,DbConnectionFactory connectionFactory)
在System.Data.SqlClient.SqlConnection.Open()
我没有在连接字符串中指定连接超时,并且此数据库中还有其他应用程序和进程。有没有人遇到过这种行为,如果有的话,采取了哪些措施来阻止它?
我的数据访问层中最常用的方法如下所示,我所有其他DAL方法都遵循相同的方法:
using (SqlConnection con = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand("AddGdsMonitorLogEntry", con))
{
cmd.CommandType = CommandType.StoredProcedure;
/* setting cmd.Parameters [snipped] */
// We have been getting some timeouts writing to the log; wait a little longer than the default.
cmd.CommandTimeout *= 4;
con.Open();
cmd.ExecuteNonQuery();
}
非常感谢!
修改
鉴于在镜像环境中发生这种情况的评论,我确实应该提到有问题的数据库是镜像的。它在SSMS中标记为“Principal,Synchronized”,“高安全性,无自动故障转移(同步)”模式。
编辑5/26/11
我在SQL Server日志中看不到任何问题。 (我无法访问该服务器上的Windows事件查看器,但我已经要求有人找我。)
答案 0 :(得分:15)
根据今天刚刚创建的MSDN Blog post(谷歌万岁!):
Microsoft已确认这是当前版本的ADO.NET中的一个问题。此问题将在ADO.NET版本中修复,随Visual Studio 2011一起提供。
与此同时,我们要求使用以下变通方法:
将连接字符串超时增加到150秒。这将使第一次尝试有足够的时间连接(150 * .08 = 12秒)
在连接字符串中添加MinPool Size = 20。这将始终在池中保持至少20个连接,并且创建新连接的机会将会减少,从而减少出现此错误的可能性。
- 醇>
提高网络性能。将NIC驱动程序更新为最新固件版本。当您的NIC卡与某些可扩展网络包设置不兼容时,我们已经看到了网络延迟。如果您使用的是Windows Vista SP1或更高版本,则还可以考虑禁用接收窗口自动调整。如果您启用了NIC绑定,则禁用它将是一个不错的选择。
帖子本身是一个有趣的读物,谈论TCP / IP连接重试算法。并且对所有说“嘿,这看起来与镜像有关......”的人们感到荣幸!请注意有关此问题的评论“因为SQL Server响应缓慢或网络延迟”。
UGH !!!
感谢所有发帖的人。现在我们都必须要求.NET Framework(或其他一些ADO.NET补丁机制)的补丁,所以我们不必等待(并购买)Visual Studio 11 ......
答案 1 :(得分:7)
连接超时与命令超时不同。命令超时适用于建立连接时的情况,但由于某些内部原因,服务器无法在所需时间内返回任何结果。默认命令超时为30秒。 http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.commandtimeout.aspx
尝试在连接字符串中指定连接超时。默认值是15秒,这可能是您看到问题的原因。 您还可以在代码中指定连接超时: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectiontimeout.aspx
答案 2 :(得分:1)
我在这个旧的数据库服务器上偶尔得到这个(现在已经10岁了)。当它确实发生的时候,因为有些东西正在不断地用连接/查询来锤击那个东西。我的猜测是你会发现,当它发生时,数据库服务器处于负载状态(或大量连接或沿着这些线路的东西)无论如何,根据我的经验,如果你可以优化代码,优化数据库,变得更加强大数据库服务器等都有帮助。 Piotr建议你可以做的另一件事就是连接超时。我仍然会经历并优化一些东西(从长远来看应该有所帮助)。
答案 3 :(得分:0)
我能够在某种程度上可靠地重现这个问题。我有一项服务,当请求处理作业时,它启动新的appdomain /线程中的处理。该线程将同时执行10到16个数据库查询。当我一个接一个地运行30个这样的作业时,随机的一个或两个作业将因超时错误而崩溃。
我更改了连接字符串以关闭连接池,使用Pooling = false,然后将错误更改为以下内容。这会在聚合异常中抛出3到4次,因为连接发生在Parallel.For
中System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
at System.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean& marsCapable)
at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity)
at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject)
at System.Data.SqlClient.SqlInternalConnectionTds.LoginWithFailover(Boolean useFailoverHost, ServerInfo primaryServerInfo, String failoverHost, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout)
at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, TimeoutTimer timeout, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup)
at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.ExecuteQuery(PtQuery query, ValueStore`1 store, String readerDescription) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 326
at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.<StockHistoricalData>b__15(PtQuery query) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 302
at System.Threading.Tasks.Parallel.<>c__DisplayClass32`2.<PartitionerForEachWorker>b__30()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass7.<ExecuteSelfReplicating>b__6(Object )
答案 4 :(得分:0)
优化您在远程服务器上执行的查询将始终有所帮助。为每个查询计时并查找长时间运行的查询。如果您只是在进行读取,那么请在SELECT语句中使用(NOLOCK)提示。这对我来说是一个救生员。只需阅读它,以确保它适合您的应用程序。如果您有权访问远程数据库,请确保indexes are not to fragmented。这将导致查询执行速度大幅下降。确保将索引作为SQL维护计划的一部分进行重建/重组。在适当的地方添加新索引。
延长超时可能会使事情更糟。如果您让查询运行时间更长,则可能会有更多查询超时。超时用于保护服务器和访问它的其他客户端。把它搞砸了并不是什么大不了的事,但是你不想让查询长时间运行来杀死服务器。