连接池可能存在切断的连接

时间:2012-03-20 23:30:58

标签: c# .net ado.net connection-pooling sqlconnection

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

  • 使用相同的连接字符串
  • 创建自己的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()));
        }
    

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

  

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

     

在System.Data.SqlClient.SqlConnection.GetOpenConnection()
  在System.Data.SqlClient.SqlConnection.get_Parser()
  在System.Data.SqlClient.SqlConnection.Open()

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

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

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

编辑#1 :代码中没有静态属性 - 我们总是在适当的时候关闭连接,在我们的Close方法中使用wasOpened并且意味着:如果在调用Open时已经打开了连接,只需将其打开关闭,否则关闭它。但是,这与此问题中描述的问题无关(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

3 个答案:

答案 0 :(得分:5)

首先,请仔细阅读:SQL Server Connection Pooling (ADO.NET)

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

  

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

     

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

     

连接池通过以下方式满足连接请求   重新分配连接,因为它们被释放回池中。 如果   已达到最大池大小且没有可用的连接   可用,请求排队。然后,捣蛋者试图收回任何   连接,直到达到超时(默认为15秒)。   如果pooler在连接时间之前无法满足请求   out,抛出异常

     

我们强烈建议您始终关闭连接   完成使用它,以便连接将返回到   池。您可以使用的Close或Dispose方法执行此操作   连接对象,或打开使用中的所有连接   C#中的语句,或Visual Basic中的Using语句。连接   未明确关闭的可能不会被添加或返回给   池。有关更多信息,请参阅using Statement(C#Reference)

简而言之:不要在连接池的范围内挖走,并在完成后立即关闭连接(fe via using-statement)。

由于您不想throw your DB-Class into the garbage can,我建议您增加最大池大小和/或超时或禁用池,看看会发生什么。

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

您还应该尝试捕捉此特定错误并clear all connection pools

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

或者看看这些看起来很有希望的问题:

答案 1 :(得分:2)

  

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

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

如果您遇到随机出现的问题,在您的情况下,根据您显示的代码,您可能会:

  1. 服务器上连接池的问题
  2. 代码中某处的竞争条件
  3. 所有这一切......

    您应该将SqlConnection包装在using语句中。这样,当您的代码或线程完成时,连接将被关闭。

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

    这样一来,连接就可以保证调用Dispose()方法(无论如何内部调用Close())。这样,如果连接正在使用(可能是它),则可以将连接返回到池中。

答案 2 :(得分:2)

Tim SchmelterBryan Crosby提供的答案在指南,参考资料,最佳做法等方面非常有价值。不幸的是,这对我来说还不够,因为我不能在遗留代码中做出重大改变。

解决此特定问题的方法是使用相同的锁封装SqlConnection的Open和Close方法。请注意,它符合我们的情况,可能不适合其他人。

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

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