2012-01-25 47 views
15

我看到了可怕的“从池中获取连接之前已超时的时间”错误。如何找到泄漏的数据库连接池句柄?

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

我想要做的是这样的:下一次我们得到这个错误,让系统转储一个列表,哪些特殊处理或http请求持有所有句柄,所以我可以找出哪些代码导致了问题。

更好的办法是查看这些手柄持续了多长时间,这样我就可以发现有用但未关闭的连接。

有没有办法做到这一点?

+0

是否有可能你有更多的用户比池中的连接? –

+0

您是否将连接引用存储在会话状态或其他地方的生命周期超过一个http请求的地方? – Jan

+0

不,他们只是打开和(据说)关闭每个请求。 – Jesse

回答

5

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

我刚才提到的一篇文章是Bill Vaughn's article(注意这是旧的,但仍然包含有用的信息)。它在监视连接池方面有一些信息,但对于哪里可能发生泄漏也有一些很好的见解。

对于监测,他建议;

“监视连接池

好了,你打开的连接和关闭它,并想知道,如果 连接还是很到位的,含情脉脉的连接池上 气垫。好了,有几种方法,以确定有多少 连接仍然到位(仍然连接),甚至他们 在做什么,我将讨论其中的一些在这里和我的书:

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

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

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

·监视代码中的性能计数器。此选项允许您显示 或仅监视连接池 的健康状况和建立的连接数。我在本文后面的部分讨论这个问题。“

编辑:

与往常一样,看看这里的一些other similar posts对SO

第二个编辑:

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

+0

如果连接在池中保持打开状态,那么如何在SQL Profiler何时成为孤儿?似乎我想要的信息不在数据库中,但在池管理层中。 – Jesse

+0

我无法评论SQL Profiler选项,因为我没有使用它来监视连接池。有关您可以从PerfMon和性能计数器中获得的信息种类的更多信息(可以在http://msdn.microsoft.com/en-us/library/ms254503.aspx找到最后一对夫妇)。在这些只有**之后**你收到“可怕的错误”不会告诉你**当他们已经成为孤儿虽然我已经使用性能计数器调查数据主要是为了确认在使用不断增长而不被回收,一旦证实,你必须追捕它 –

+0

嗯,不是我正在寻找的银弹,但我很欣赏彻底的解释! – Jesse

13

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

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

这帮助我们找到了一个打开,永不关闭的连接真是很酷的课:)非常感谢! – Challe

+0

太棒了!很高兴知道这可能会对其他人有用。 – LOAS