可能断开连接的连接池

本文关键字:连接 断开 | 更新日期: 2023-09-27 18:28:22

我有多个线程访问同一个数据库(使用相同的连接字符串)。每个线程:

  • 使用相同的连接字符串创建自己的SqlConnection实例
  • 只要需要一个,就使用下面的代码打开自己的连接实例

        try
        {
            wasOpened = connection.State == ConnectionState.Open;
            if (connection.State == ConnectionState.Closed)
            {
                connection.Open();
            }
        }
        catch (Exception ex)
        {
            throw new Exception(string.Format("Connection to data source {0} can not be established! Reason: {1} - complete stack {2}",
                                              connection.Database, ex.Message, ex.StackTrace == null ? "NULL" : ex.StackTrace.ToString()));
        }
    

到目前为止,我们已经在两台服务器上测试了这段代码,其中一台服务器有时会在SqlConnection.Open方法中抛出异常。以下是我们从catch块得到的异常消息:

无法建立到数据源xyz的连接!原因:操作无效。连接已关闭。-全栈

位于System.Data.SqlClient.SqlConnection.GetOpenConnection()
位于System.Data.SqlClient.SqlConnection.get_Parser()
位于System.Data.SqlClient.SqlConnection.Open()

检查SqlConnection.GetOpenConnection方法显示innerConnection为null:

internal SqlInternalConnection GetOpenConnection()
{
    SqlInternalConnection innerConnection = this.InnerConnection as SqlInternalConnection;
    if (innerConnection == null)
    {
        throw ADP.ClosedConnectionError();
    }
    return innerConnection;
}

我一直不清楚:为什么连接池有时会给我断开连接(innerConnection==null)?

编辑#1:代码中没有静态属性-我们总是在适当的时候关闭连接,wasOpened在我们的Close方法中使用,意思是:如果在调用Open时连接已经打开,请在Close时保持打开状态,否则关闭它。然而,这与本问题中描述的问题无关(innerConnection==null)。

编辑#2:服务器:SQL Server 2008 R2,Windows Server 2003。客户端:Windows Server 2003(代码在SSIS包自定义组件中运行)。连接字符串:Data Source=server_name;Initial Catalog=db_name;Integrated Security=SSPI;Application Name=app_name

可能断开连接的连接池

首先,请仔细阅读:SQL Server连接池(ADO.NET)

我会引用最重要的部分给你(我认为):

为每个唯一的连接字符串创建一个连接池。当创建池,创建多个连接对象并将其添加到以便满足最小池大小要求。根据需要将连接添加到池中,最多可添加到最大池指定大小(默认值为100)。连接被释放回来当它们被关闭或处置时,进入池

当请求SqlConnection对象时,会从池中获取该对象如果可用的连接可用。要可用,连接必须未使用、具有匹配的事务上下文或与任何事务上下文,并且具有到服务器的有效链接。

连接池程序通过以下方式满足连接请求在将连接释放回池时重新分配连接如果已达到最大池大小,没有可用的连接如果可用,请求将排队。然后,台球手试图收回任何连接,直到达到超时(默认值为15秒)。如果池程序无法在连接时间之前满足请求out时,将抛出一个异常。

我们强烈建议您在已完成使用,以便将连接返回到水塘可以使用的Close或Dispose方法执行此操作Connection对象,或通过打开使用C#中的语句或Visual Basic中的Using语句。连接未显式关闭的可能不会添加或返回到水塘有关更多信息,请参阅使用语句(C#参考)

简而言之:不要在连接池的区域内挖走,并在完成连接后立即关闭连接(例如通过using-statement)。

既然你不想把你的DB类扔进垃圾桶,我建议你要么增加最大池大小和/或超时,要么禁用池,看看会发生什么。

<add name="theConnectionString" connectionString="Data Source=(local);
     Database=AdventureWorks; Integrated Security=SSPI; 
     Max Pool Size=200; Pooling=True; Timout=60" />

您还应该尝试捕获此特定错误并清除所有连接池:

System.Data.SqlClient.SqlConnection.ClearAllPools();

或者看看这些看起来很有前景的问题:

  • SQL Server连接池没有';检测不到闭合的连接
  • http://forums.asp.net/t/1729521.aspx/1

我有多个线程访问同一个数据库(使用相同的连接字符串)。每个线程:

  1. 使用相同的连接字符串创建自己的SqlConnection实例
  2. 使用下面的代码在需要时打开自己的连接实例

如果你有一个随机出现的问题,在你的情况下,根据你显示的代码,你可能有:

  1. 服务器上的连接池出现问题
  2. 代码中的某个竞赛条件

话虽如此。。。

您应该将SqlConnection封装在using语句中。这样,当你的代码或线程完成后,连接就会关闭

using (SqlConnection connection = new SqlConnection(connectionString))
{
   //... stuff
}

通过这种方式,连接可以保证调用Dispose()方法(该方法在内部无论如何都会调用Close())。这样,如果连接正在使用(可能正在使用),则可以将连接返回到池。

Tim Schmelter和Bryan Crosby提供的答案在指导方针、参考资料、最佳实践等方面都非常有价值。不幸的是,这对我来说还不够,因为我无法对遗留代码进行突破性的更改。

这个特殊问题的解决方案是用相同的锁封装SqlConnection的Open和Close方法。请注意,它适合我们的场景,可能不适合其他人。

我真的很抱歉,我现在不能更深入地研究这个问题,并找出问题的根源是我们的代码还是连接池不是完全线程安全的。我知道最有可能的来源在我们的代码中。考虑到这一点,这个答案更多的是变通方法,而不是真正的解决方案。

在任何人应用此解决方法之前,请阅读其他答案。