25

我有一个使用 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 中标记为“主体,同步”,处于“无自动故障转移(同步)的高安全性”模式。

编辑 2011 年 5 月 26 日

我在 SQL Server 日志中没有看到任何问题。(我无权访问该服务器上的 Windows 事件查看器,但我已要求有人来找我。)

4

5 回答 5

15

根据今天刚刚创建的MSDN 博客文章(谷歌万岁!):

Microsoft 已确认这是当前版本的 ADO.NET 中存在的问题。此问题将在 Visual Studio 2011 附带的 ADO.NET 版本中得到修复。

同时,我们要求使用以下解决方法:

  1. 将连接字符串超时增加到 150 秒。这将为第一次尝试提供足够的连接时间(150* .08=12 秒)

  2. 在连接字符串中添加 MinPool Size=20。这将始终在池中保持至少 20 个连接,并且创建新连接的机会将更少,从而减少此错误的机会。

  3. 提高网络性能。将您的 NIC 驱动程序更新到最新的固件版本。当您的 NIC 卡与某些 Scalable Networking Pack 设置不兼容时,我们发现了网络延迟。如果您使用的是 Windows Vista SP1 或更高版本,您还可以考虑禁用 Receive Window Auto-Tuning。如果您启用了 NIC 分组,则禁用它是一个不错的选择。

这篇文章本身很有趣,讨论了 TCP/IP 连接重试算法。向所有说“嘿,这看起来与镜像有关……”的人表示敬意!并注意关于“由于 SQL Server 响应缓慢或网络延迟”的评论。

啊!!!

感谢所有发帖的人。现在我们都必须要求为 .NET Framework(或其他一些 ADO.NET 修补机制)提供补丁,这样我们就不必等待(并购买)Visual Studio 11...

于 2011-05-26T21:24:11.397 回答
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

于 2011-05-15T14:02:29.710 回答
1

我每隔一段时间就会在我们拥有的这个旧数据库服务器上得到这个(现在已经 10 岁了)。当它确实发生时,这是因为某些东西正在通过连接/查询不断地敲打那个东西。我的猜测是,当它发生时,您会发现数据库服务器处于负载状态(或大量连接或类似的东西)无论如何,根据我的经验,如果您可以优化代码,优化数据库,获得更强大数据库服务器等都有帮助。Piotr 建议您可以做的另一件事就是延长连接的超时时间。不过,我仍然会经历并优化一些东西(从长远来看应该会有所帮助)。

于 2011-05-20T14:09:10.500 回答
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 )
于 2011-05-15T21:38:53.563 回答
0

优化您在远程服务器上执行的查询总是有帮助的。为每个查询计时并寻找长时间运行的查询。如果您只是进行读取,则在 SELECT 语句上使用(NOLOCK)提示。这对我来说是一个救命稻草。只需阅读它以确保它适合您的应用程序。如果您有权访问远程数据库,请确保索引没有碎片化。这将导致查询执行速度大大减慢。确保作为 SQL 维护计划的一部分重建/重组索引。在适当的地方添加新索引。

延长超时可能会使事情变得更糟。如果您让查询运行更长时间,则可能会有更多查询超时。超时是为了保护服务器和其他访问它的客户端。稍微提高一点并不是什么大不了的事,但是您不希望长时间运行的查询会杀死服务器。

于 2011-05-20T15:30:34.087 回答