【问题标题】:Managing database connectivity with ADO.NET使用 ADO.NET 管理数据库连接
【发布时间】:2010-09-25 11:17:12
【问题描述】:

我们有一个基于 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 的文本是:

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

【问题讨论】:

  • InvalidOperationException 的 .Message(或 .ToString())中的文本是什么?
  • 这是 winforms 还是 asp.net 应用程序?
  • 你能发布连接字符串吗? (很明显XXX去掉用户名/密码和数据库名来保护无辜)

标签: .net ado.net timeout


【解决方案1】:

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

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

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

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

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

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

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

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

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

我开始怀疑我为什么要使用连接池...

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

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

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

HTH

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

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

      【讨论】:

      • 在这些情况下我使用 cmets。 :-)
      • 您也可以在收到反馈后返回并编辑此答案以成为真实答案。所以这个非答案的答案可以成为一个真正的答案。 ;-)
      【解决方案4】:

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

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

      您在 GetConnection 方法中的代码也会吞噬 DbException。

      【讨论】:

      • 连接仅在 GetConnection() 方法中打开。我们从不在其他任何地方打开连接。事实上,它第一次失败,然后干净地运行了一段时间。关于代码 - 它比其他任何东西都更像伪代码。实际代码中有实际处理,但我不想在这里复制。
      • 好的,但是如果你不复制真实的代码,任何人都很难找到错误......
      猜你喜欢
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-18
      • 2014-11-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多