web-dev-qa-db-ja.com

リークしているdb接続プールハンドルを見つける方法は?

恐ろしい「プールから接続を取得する前にタイムアウト期間が経過しました」というエラーが表示されます。

閉じられていないdb接続をコードで検索しましたが、見つかりませんでした。

私がしたいのはこれです:次にこのエラーが発生したときに、どのprocまたはhttpリクエストがすべてのハンドルを保持しているかのリストをシステムにダンプさせて、問題の原因となっているコードを特定できるようにします。

さらに良いのは、これらのハンドルがどれだけ長く保持されているかを確認することです。そうすれば、使用されているが閉じられていない接続を見つけることができます。

これを行う方法はありますか?

17
Jesse

接続プールを監視するための適切なリンクがいくつかあります。 「.net接続プールの監視」をグーグル検索してください。

私がしばらく前に参照した1つの記事は Bill Vaughnの記事 (これは古いですが、まだ有用な情報が含まれていることに注意してください)。接続プールの監視に関する情報がいくつかありますが、リークが発生する可能性のある場所に関する優れた洞察もいくつかあります。

監視については、彼は提案します。

「接続プールの監視

さて、あなたは接続を開いて閉じ、接続がまだ行われているかどうかを知りたいと思います。エアマットレスの接続プールで苦しんでいます。さて、まだ接続されている(接続されたままの)接続の数、さらにはそれらが何をしているかを判断する方法はいくつかあります。私はこれらのいくつかをここと私の本で議論します:

・トレース用のSQLProfilerTSQL_ReplayテンプレートとともにSQLプロファイラーを使用します。プロファイラーに精通している方は、SP_WHOを使用してポーリングするよりも簡単です。

・SP_WHOまたはSP_WHO2を実行します。これらは、各プロセスの現在のステータスを示すすべての作業プロセスのsysprocessesテーブルから情報を返します。通常、接続ごとに1つのSPIDサーバープロセスがあります。接続文字列でApplicationName引数を使用して接続に名前を付けた場合、簡単に見つけることができます。

・パフォーマンスモニター(PerfMon)を使用して、プールと接続を監視します。次に、これについて詳しく説明します。

・コード内のパフォーマンスカウンターを監視します。このオプションを使用すると、接続プールの状態と確立された接続の数を表示または単に監視できます。これについては、このペーパーの次のセクションで説明します。」

編集:

いつものように、いくつかの 他の同様の投稿 ここSOでチェックしてください

2回目の編集:

接続がプールによって再利用されていないことを確認したら、StateChangeイベントを利用して、接続がいつ開いたり閉じたりするかを確認することもできます。閉じた状態よりも開いた状態の方がはるかに多くの状態変化があることがわかった場合は、どこかにリークがあることを示しています。次に、タイムスタンプとともにstatechangedイベントのデータをログに記録することもできます。アプリケーションに他のログが記録されている場合は、ログファイルの解析を開始して、閉じた状態から開いた状態に変化したように見えるインスタンスを見つけることができます。対応するオープンからクローズまではありません。 StateChangedEventの処理方法の詳細については、 このリンク を参照してください。

5
Mr Moose

幸運にも接続の作成/開始が一元化されている場合は、次のクラスを使用すると、リークした接続を簡単に見つけることができます。楽しい :)

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

私はこれを使用しました

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

以前に長時間実行されていたストアドプロシージャを見つけるために、戻ってSPを呼び出したメソッドを見つけることができます。

それが役立つかどうかわからない

0
kolin