2012-01-25 80 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