如何找到泄漏的数据库连接池句柄?

时间:2012-01-25 14:46:34

标签: asp.net connection-pooling

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

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

我想要做的是:下次我们收到此错误时,让系统转储一个列表,其中包含哪些proc或http请求包含所有句柄,因此我可以找出导致问题的代码。< / p>

更好的是看看这些句柄持有多长时间,所以我可以发现已使用但未封闭的连接。

有没有办法做到这一点?

3 个答案:

答案 0 :(得分:15)

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

/// <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
}

答案 1 :(得分:5)

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

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

对于监控,他建议;

  

“监控连接池

     

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

     

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

     

·运行SP_WHO或SP_WHO2,它们从中返回信息   显示当前状态的所有工作进程的sysprocesses表   每个过程。通常,每个SPID服务器进程一个   连接。如果使用“应用程序名称”命名连接   在连接字符串中的参数,它很容易找到。

     

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

     

·在代码中监控性能计数器。此选项允许   您要显示或只是监视连接池的运行状况   和已建立的连接数。我在讨论这个   本文的后续部分。“

修改:

与往常一样,在SO

上查看一些other similar posts

第二次修改:

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

答案 2 :(得分:0)

我用过这个

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

以前找到长时间运行的存储过程,然后我可以返回并找到调用SP的方法。

不知道这是否有帮助