【问题标题】:"open/close" SqlConnection or keep open?“打开/关闭” SqlConnection 还是保持打开状态?
【发布时间】:2011-05-25 06:36:34
【问题描述】:

我在简单的静态类中使用静态方法实现了我的业务逻辑。这些方法中的每一个都在调用时打开/关闭 SQL 连接:

public static void DoSomething()
{
    using (SqlConnection connection = new SqlConnection("..."))
    {
        connection.Open();

        // ...

        connection.Close();
    }
}

但我认为传递连接对象并避免打开和关闭连接节省性能。我很久以前用 OleDbConnection 类进行了一些测试(不确定 SqlConnection),它肯定有助于像这样工作(据我所知):

//pass around the connection object into the method
public static void DoSomething(SqlConnection connection)
{
    bool openConn = (connection.State == ConnectionState.Open);
    if (!openConn)
    {
        connection.Open();
    }

    // ....

    if (openConn) 
    {
        connection.Close();
    }
}

所以问题是 - 我应该选择方法 (a) 还是方法 (b) ?我在另一个 stackoverflow 问题中读到,连接池为我节省了性能,我根本不必费心......

PS。它是一个 ASP.NET 应用程序 - 连接仅在 Web 请求期间存在。不是获胜应用或服务。

【问题讨论】:

  • 只是一个建议:使用DbConnection.StateChange 事件来监控连接的状态变化(并且可能存储在本地)而不是直接检查DbConnection.State 属性。它将为您节省性能成本。
  • 缺少的一个细节是此方法如何成为页面请求的一部分。它是唯一调用的方法,还是正如我在回复中所假设的那样,它是在页面请求中调用的众多方法之一,它会影响哪个答案是正确的;)
  • David - 很多这样的方法被调用:)
  • 案例 A 表明对 Dispose 缺乏信心:参见 stackoverflow.com/questions/1195829/… 和 MSDN 上的示例 msdn.microsoft.com/en-us/library/…

标签: c# sqlconnection


【解决方案1】:

每次都使用方法 (a)。当您开始扩展您的应用程序时,如果您不这样做,处理状态的逻辑将变得非常痛苦。

连接池按照它所说的做。想想当应用程序扩展时会发生什么,以及手动管理连接打开/关闭状态有多难。连接池可以很好地自动处理这个问题。如果您担心性能,请考虑某种内存缓存机制,以免阻塞。

【讨论】:

    【解决方案2】:

    通常你应该为每个事务保持一个连接(没有并行计算)

    例如,当用户执行收费操作时,您的应用程序需要先找到用户的余额并更新它,他们应该使用相同的连接。

    即使ado.net有自己的连接池,调度连接成本很低,但复用连接是更好的选择。

    为什么不在应用程序中只保留一个连接

    因为执行某些查询或命令时连接被阻塞, 这意味着您的应用程序同时只执行一个数据库操作, 性能有多差。

    还有一个问题是即使您的用户只是打开它但没有操作,您的应用程序将始终有连接。如果有很多用户打开您的应用程序,数据库服务器将很快花费其所有连接源,而您的用户什么都没做。

    【讨论】:

      【解决方案3】:

      免责声明:我知道这已经过时了,但我找到了一种简单的方法来证明这一事实,所以我投入了两美分。

      如果您不相信池化真的会更快,那么试试这个:

      在某处添加以下内容:

      using System.Diagnostics;
      public static class TestExtensions
      {
          public static void TimedOpen(this SqlConnection conn)
          {
              Stopwatch sw = Stopwatch.StartNew();
              conn.Open();
              Console.WriteLine(sw.Elapsed);
          }
      }
      

      现在将所有对Open() 的调用替换为TimedOpen() 并运行您的程序。现在,对于您拥有的每个不同的连接字符串,控制台(输出)窗口将打开一个长时间运行的窗口,然后打开一堆 非常 快速打开。

      如果你想给他们加标签,你可以在对WriteLine的调用中添加new StackTrace(true).GetFrame(1) +

      【讨论】:

        【解决方案4】:

        在完成连接后始终关闭连接,这样它们底层的数据库连接就可以返回到池中并可供其他调用者使用。连接池的优化非常好,因此这样做不会有明显的损失。建议与交易基本相同 - 完成后保持简短并关闭。

        如果您在使用多个连接的代码周围使用单个事务时遇到 MSDTC 问题,情况会变得更加复杂,在这种情况下,您实际上必须共享连接对象,并且只有在事务完成后才将其关闭。

        但是,您在这里是手动操作的,因此您可能需要研究为您管理连接的工具,例如 DataSet、Linq to SQL、Entity Framework 或 NHibernate。

        【讨论】:

        • 您不应该在每个方法调用中正常打开和关闭连接,每个页面请求只打开和关闭一次。这就是我至少学到的;)打开和关闭需要时间。
        • @David Martensson - 当您调用 SqlConnection.Open 时,连接实际上并没有打开和关闭。当连接字符串与以前使用的连接字符串匹配时,ASP.NET 会从池中回收活动连接。所涉及的开销是无关紧要的,此外,尝试“自己做”意味着您必须承担所有管理任务,以确保每次后续使用时连接仍然处于活动状态,这增加了复杂性和开销。对于连接池,最佳做法是每次使用都打开和关闭它。
        • 恕我直言,“始终关闭连接”的答案不太适合这个问题......我确实关闭了它们。问题是 - 什么时候。
        • @David Martensson “每页一次”过于简单化了。你是对的,如果你有几个数据库命令一个接一个地执行,你可以在执行它们时保持连接打开。如果您关闭并重新打开,则会产生少量开销 - 连接将进入池并稍后从池中检索。
        • @David Martensson 但永远不要保持空闲连接。如果您正在等待用户的操作或其他任何操作,请将其关闭。如果有疑问,请关闭它。您尽可能晚地打开,希望其他人已经完成连接并汇集它。然后你回报的青睐 - 尽可能早地关闭。
        【解决方案5】:

        物理连接和逻辑连接是有区别的。 DbConnection 是一种逻辑连接,它使用与 Oracle 的底层物理连接。关闭/打开 DbConnection 不会影响您的性能,但会使您的代码干净和稳定 - 在这种情况下连接泄漏是不可能的。

        您还应该记住在数据库服务器上存在并行连接限制的情况 - 考虑到这一点,有必要使您的连接非常短。

        连接池将您从连接状态检查中解放出来 - 只需打开、使用并立即关闭它们。

        【讨论】:

        • 是的,连接不是连接——即 DbConnection 不是物理连接。 DbConnection 是一个 .NET 类,它提供了操作底层物理连接的方法和属性。
        • 不幸的是,这并不是很明显,这一切都是隐式完成的,但文档对此进行了详细说明。 docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
        【解决方案6】:

        坚持选项a

        连接池是你的朋友。

        【讨论】:

        • 恕我直言-他甚至不应该接近。处置会做到这一点。
        • @RoyiNamir 我有点喜欢关闭连接的调用。特别是对于代码库的初学者和新手。它更加明确和可读。
        • @edhedges 同时使用“using”和 Close() 最终只会让新手感到困惑。他们不会理解使用“使用”的目的。不要使用“关闭”,而是教他们“使用”的目的。这样他们就可以学习得更好,并将他们学到的知识应用到代码的其他部分。
        • 应该/是否需要调用“Open()”?我目前正在这样使用它: using (var conn = GetConnection()) {} public SqlConnection GetConnection() { return new SqlConnection(_connectionString); }
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-19
        • 2016-09-09
        • 2013-10-21
        • 2011-09-14
        相关资源
        最近更新 更多