2

我们有一个基于 ADO.NET 构建的应用程序。我们遵循一些简单的最佳实践,使我们能够利用连接池。例如,使用数据库的代码块可能如下所示:

using( DbConnection dbConnection = GetDatabaseConnection() ) {   
    doWork();
}

FWIW,GetDatabaseConnection 没有什么特别之处。它与运行 MSSQL Server 2000 的数据库建立连接。实际上,它的内容如下:

DbConnection GetDatabaseConnection() {
    return GetConnection(MyConnectionString);
}

DbConnection GetConnection(String connectionString)
{
  try {
      SqlConnection dbConnection = new SqlConnection(connectionString);
      dbConnection.Open();
      return dbConnection;
  } catch( InvalidOperationException ex ) {
      handleError(ex);
      throw;
  } catch( DbException ex ) {
      handleError(ex);
  }
}

因此,我们的连接在块作用域的末尾被处理掉了。然而,当我们开始测试应用程序时,我们遇到了一个小故障。我们发现我们的应用程序非常突发,这意味着有时它会变得非常健谈,然后会沉默一段时间。结果是我们可以同时拥有多个线程来获取连接。

所以假设你有 10 个线程。一批工作(请不要试图改写这批工作)到达并被分割成几个线程。然后每个线程都尝试获得连接和繁荣,我遇到了 InvalidOperationException。我已经调整了 ConnectTimeout,所做的只是延长时间,直到我遇到一系列异常。一旦我通过了“结束”阶段,应用程序就很好了。然后它再次停顿,连接“消失”并且该过程再次开始。

我也尝试过调整 LoadBalanceTimeout 但异常继续出现。你们中有人见过这个问题吗?任何想法......我会扔掉我自己的几个。

  • 持续保持一些连接“热”
  • 尝试再次打开连接,最多尝试 # 次
  • 实现我自己的连接池(哎呀,对重新发明轮子不感兴趣)

编辑:

我读过的大多数论坛都不鼓励增加连接池的大小。默认情况下,连接池的上限为 50 个连接(这绰绰有余——如果我必须增加它,其他地方就会出现根本问题)。我注意到的是,当 ConnectTimeout 较低时,会发生 InvalidOperationException。就好像连接的启动时间太长并且待处理的连接都超时了。

MARS 当然是一个选项... InvalidOperationException.Message 的文本是:

超时已过。在从池中获取连接之前超时时间已过。这可能是因为所有池连接都在使用中并且达到了最大池大小。

4

4 回答 4

2

从 MSDN ( http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx ):

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

连接池程序通过在连接释放回池时重新分配连接来满足连接请求。如果已达到最大池大小并且没有可用的连接可用,则将请求排队。然后,池程序尝试回收任何连接,直到达到超时(默认值为 15 秒)。如果 pooler 在连接超时之前无法满足请求,则会抛出异常

翻译: 检查您的事务上下文...如果您的池大小为 10 个连接,并且在不同的事务下创建了 10 个连接,那么您就完蛋了。

请注意,只有在尝试与服务器通信后才能检测到断开的连接。如果发现不再连接到服务器的连接,则将其标记为无效。无效连接仅在关闭或回收时才会从连接池中删除。

如果存在与已消失的服务器的连接,即使连接池程序没有检测到断开的连接并将其标记为无效,也可以从池中提取此连接。之所以出现这种情况,是因为检查连接是否仍然有效的开销会通过导致发生与服务器的另一次往返来消除拥有池化程序的好处。发生这种情况时,第一次尝试使用连接会检测到连接被切断,并抛出异常

翻译: 你真的不能靠一个连接来连接吗?这篇文章并没有真正解释如何处理这个......

您可以尝试偶尔使用 ClearAllPools 和 ClearPool 手动清除池,但这对我来说仍然像是创可贴,让我畏缩。

该文章还讨论了安全上下文,说:
通过调用 sp_setapprole 系统存储过程激活 SQL Server 应用程序角色后,无法重置该连接的安全上下文。但是,如果启用了池化,则将连接返回到池中,并且在重新使用池化连接时会发生错误。

我开始想知道为什么我使用连接池......

最后:
由于集成安全
性导致 的池碎片连接根据连接字符串和用户身份进行池化。因此,如果您在网站上使用基本身份验证或 Windows 身份验证以及集成的安全登录,您将获得每个用户一个池。尽管这提高了单个用户后续数据库请求的性能,但该用户无法利用其他用户建立的连接。它还导致每个用户至少有一个到数据库服务器的连接。

因此,如果您在 Web 应用程序上使用集成安全性,如果您有足够的用户,您可以填满您的连接池。

在不了解您的应用程序的更多细节的情况下,很难放大可能会绊倒您的事情,但希望这可以为您提供一些想法。

高温高压

于 2008-12-05T20:50:23.410 回答
0

您可以尝试将“最大池大小”设置得更高。此外,您可能想尝试在连接上显式调用“关闭”。

于 2008-12-05T04:58:30.697 回答
0

问题 1:您的 GetDatabaseConnection() 方法是否会派生一个新线程来生成数据库连接并将其返回给主线程......或者是多个线程试图访问该方法的这个实例......或者这个方法是仅从池中获取连接的共享/静态方法?

问题 2:您的数据库服务器是什么品牌和型号?SQL 服务器?甲骨文?PostgreSQL?

问题 3:您是否需要在不同的连接上完成所有工作,或者如果所有工作都可以在一个连接上完成,这就足够了吗?如果是 SQL Server,那么 MARS 可能允许单个连接上的多个记录集可能会有所帮助,但对于您的应用程序可能不可行。

问题4:返回的异常中的细节是什么?

只是想清楚地了解您的应用程序架构是如何工作的......

(PS有谁知道我如何将“答案”标记为“不是答案,而是要求进一步澄清”......即这个“答案”)

于 2008-12-05T05:29:36.377 回答
0

根据 MSDN,如果您尚未指定数据源或服务器,或者连接已打开,则 SqlConnection.Open 会引发 InvalidOperationException。

您确定在“doWork”方法中没有对 SqlConnection.Open 的任何调用吗?

此外,您在 GetConnection 方法中的代码会吞下 DbException。

于 2008-12-05T08:49:54.037 回答