【问题标题】:How to find leaking db connection pool handle?如何找到泄漏的数据库连接池句柄?
【发布时间】:2012-01-25 14:46:33
【问题描述】:

我看到了可怕的“从池中获取连接之前已过超时时间”错误。

我已经在代码中搜索了任何未关闭的数据库连接,但找不到。

我想做的是:下次我们收到此错误时,让系统转储一个列表,其中列出了哪些 proc 或 http 请求持有所有句柄,这样我就可以找出导致问题的代码。

最好看看这些把手被握了多长时间,这样我就可以发现使用过但未关闭的连接。

有什么办法吗?

【问题讨论】:

  • 池中的用户是否可能多于连接数?
  • 您是否将连接引用存储在会话状态或其他生命周期超过一个 http 请求的地方?
  • 不,它们只是在每次请求时打开和(据说)关闭。
  • 非常相似的问题 - stackoverflow.com/questions/1600718/…
  • 您要连接的数据库是什么?否则,您运行的服务器是否有可能比池中的最大连接数多于执行线程(服务请求)?

标签: asp.net connection-pooling


【解决方案1】:

如果你足够幸运,连接创建/打开是集中的,那么下面的类应该可以很容易地发现泄漏的连接。享受:)

using System.Threading; // not to be confused with System.Timer
/// <summary>
/// This class can help identify db connection leaks (connections that are not closed after use).
/// Usage:
/// connection = new SqlConnection(..);
/// connection.Open()
/// #if DEBUG
/// new ConnectionLeakWatcher(connection);
/// #endif
/// That's it. Don't store a reference to the watcher. It will make itself available for garbage collection
/// once it has fulfilled its purpose. Watch the visual studio debug output for details on potentially leaked connections.
/// Note that a connection could possibly just be taking its time and may eventually be closed properly despite being flagged by this class.
/// So take the output with a pinch of salt.
/// </summary>
public class ConnectionLeakWatcher : IDisposable
{
    private readonly Timer _timer = null;

    //Store reference to connection so we can unsubscribe from state change events
    private SqlConnection _connection = null;

    private static int _idCounter = 0;
    private readonly int _connectionId = ++_idCounter;

    public ConnectionLeakWatcher(SqlConnection connection)
    {
        _connection = connection;
        StackTrace = Environment.StackTrace;

        connection.StateChange += ConnectionOnStateChange;
        System.Diagnostics.Debug.WriteLine("Connection opened " + _connectionId);

        _timer = new Timer(x =>
        {
            //The timeout expired without the connection being closed. Write to debug output the stack trace of the connection creation to assist in pinpointing the problem
            System.Diagnostics.Debug.WriteLine("Suspected connection leak with origin: {0}{1}{0}Connection id: {2}", Environment.NewLine, StackTrace, _connectionId);
            //That's it - we're done. Clean up by calling Dispose.
            Dispose();
        }, null, 10000, Timeout.Infinite);
    }

    private void ConnectionOnStateChange(object sender, StateChangeEventArgs stateChangeEventArgs)
    {
        //Connection state changed. Was it closed?
        if (stateChangeEventArgs.CurrentState == ConnectionState.Closed)
        {
            //The connection was closed within the timeout
            System.Diagnostics.Debug.WriteLine("Connection closed " + _connectionId);
            //That's it - we're done. Clean up by calling Dispose.
            Dispose();
        }
    }

    public string StackTrace { get; set; }

    #region Dispose
    private bool _isDisposed = false;

    public void Dispose()
    {
        if (_isDisposed) return;

        _timer.Dispose();

        if (_connection != null)
        {
            _connection.StateChange -= ConnectionOnStateChange;
            _connection = null;
        }

        _isDisposed = true;
    }

    ~ConnectionLeakWatcher()
    {
        Dispose();
    }
    #endregion
}

【讨论】:

  • 这帮助我们找到了一个已打开但从未关闭的连接。真的很酷的课:) 非常感谢!
  • 太棒了!很高兴知道这对其他人有用。
【解决方案2】:

有一些很好的链接可以监控连接池。谷歌搜索“.net 连接池监控”。

我不久前提到的一篇文章是Bill Vaughn's article(注意这是旧的但仍然包含有用的信息)。它有一些关于监控连接池的信息,但也有一些关于可能发生泄漏的地方的深刻见解。

对于监控,他建议;

"监控连接池

好的,所以您打开了一个连接并关闭了它,并想知道是否 连接仍然存在——在连接池中 气垫。好吧,有几种方法可以确定有多少 连接仍然存在(仍然连接),甚至它们 是做。我在这里和我的书中讨论了其中的几个:

· 将 SQL Profiler 与 SQLProfiler TSQL_Replay 一起使用 跟踪模板。对于那些熟悉 Profiler 的人, 这比使用 SP_WHO 轮询更容易。

· 运行 SP_WHO 或 SP_WHO2,它们从 显示当前状态的所有工作进程的 sysprocesses 表 每个过程的。一般情况下,每个服务器都有一个 SPID 服务器进程 联系。如果您为连接命名,请使用应用程序名称 连接字符串中的参数,很容易找到。

· 使用性能监视器 (PerfMon) 来监视池 和连接。接下来我会详细讨论。

· 监控代码中的性能计数器。此选项允许 您可以显示或简单地监控连接池的运行状况 以及已建立的连接数。我在一个 本文后续部分。”

编辑:

和往常一样,在 SO 上查看一些 other similar posts

第二次编辑:

一旦您确认连接没有被池回收,您可以尝试的另一件事是利用 StateChange 事件来确认何时打开和关闭连接。如果您发现打开的状态更改比关闭的多得多,则表明某处存在泄漏。然后,您还可以将数据与时间戳一起记录在 statechanged 事件中,如果您的应用程序上有任何其他日志记录,您可以开始解析日志文件以查找似乎关闭状态更改为打开的实例,使用没有对应的开到关。有关如何处理 StateChangedEvent 的更多信息,请参阅this link

【讨论】:

  • 如果连接在池中保持打开状态,您如何在 SQL Profiler 中看到它们已被孤立?似乎我想要的信息不在数据库中,而是在池管理层。
  • 我无法评论 SQL Profiler 选项,因为我没有使用它来监控连接池。有关您可以从 PerfMon 和性能计数器中获得的信息类型的更多信息(他谈到的最后一对可以在 msdn.microsoft.com/en-us/library/ms254503.aspx> 找到。仅查看这些之后您会收到“可怕的错误”不会告诉你什么时候他们已经被孤立了。我已经使用性能计数器轮询了数据,主要是为了确认正在使用的连接数量不断增长而没有被回收。一旦确认,你必须追捕它。
  • 嗯。不完全是我正在寻找的灵丹妙药,但我很欣赏详尽的解释!
【解决方案3】:

我用过这个

http://www.simple-talk.com/sql/performance/how-to-identify-slow-running-queries-with-sql-profiler/

之前要查找长时间运行的存储过程,然后我可以回过头来找到调用 SP 的方法。

不知道有没有用

【讨论】:

  • 我遇到的问题(我认为)不在于运行缓慢的查询——它在于运行查询的代码,然后不放开数据库句柄。
猜你喜欢
  • 1970-01-01
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多