sql >> データベース >  >> RDS >> Database

アプリケーションのデータベース接続リークを見つける

    ゲスト作成者:Michael J Swart(@MJSwart)

    最近、アプリケーションがスローしたいくつかの例外に驚いていました。 SqlConnectionを開こうとしたときに、アプリケーションが失敗していました。例外は次のようになりました:

    エラーSystem.InvalidOperationException:

    タイムアウトが期限切れになりました。プールから接続を取得する前にタイムアウト期間が経過しました。これは、プールされたすべての接続が使用されていて、最大プールサイズに達したために発生した可能性があります。

    接続プール

    .Netは接続プールを使用して、すべてのクエリで接続を確立するオーバーヘッドを回避できることを忘れないでください。接続プールは接続文字列ごとに維持され、デフォルトではプール内の接続数は100に制限されています。通常、100の接続で十分です。これまでこの例外で問題が発生したことはなく、サーバーは通常よりもビジー状態ではなかったため、MaxPoolSizeの値を増やすことを躊躇していました。データベース接続のリークが疑われ始めました。

    データベース接続のリーク

    メモリリークと同様に、データベース接続をタイムリーに破棄しないと、データベース接続リークが発生する可能性があります。 SqlConnectionsはIDisposableであるため、usingステートメントを使用することをお勧めします:

    using (SqlConnection conn = new SqlConnection(connectionString)) 
    {
      conn.Open();
      // etc...
    }

    SqlConnectionの使用が完了するとすぐに破棄され、実際の接続はすぐに接続プールに戻るため、他のユーザーが使用できるようになります。それ以外の場合、接続は、プロセスが終了するか、ガベージコレクションによってクリーンアップされるまで使用され続けます。

    接続リークの検出

    そのため、データベース接続のリークが原因でアプリケーションで接続タイムアウトが発生した場合、スタックトレースが役に立たない可能性があります。メモリリークによるメモリ不足の例外と同様に、スタックトレースには被害者に関する情報がありますが、根本的な原因はありません。では、どこでリークを見つけることができますか?

    データベース接続のリークはクライアントの問題ですが、データベースサーバーからヘルプを見つけることができます。データベースサーバーで、データベースごとのプロセスごとの接続を調べて、各プールのサイズの概算を取得します。

    select count(*) as sessions,
             s.host_name,
             s.host_process_id,
             s.program_name,
             db_name(s.database_id) as database_name
       from sys.dm_exec_sessions s
       where is_user_process = 1
       group by host_name, host_process_id, program_name, database_id
       order by count(*) desc;

    プログラム名、ホスト名、プロセスID、データベース名は通常、同じ接続プールからの接続を識別するのに十分です。

    これにより、多くの接続があるプールについてさらにいくつか質問することになります。プールがある場合、しばらくスリープしているセッションはありますか?ある場合、それらはどのくらいスリープしていて、最後に実行したSQLステートメントは何でしたか?

    declare @host_process_id int = 1508;
      declare @host_name sysname = N'SERV4102';
      declare @database_name sysname = N'My_Database';
     
      select datediff(minute, s.last_request_end_time, getdate()) as minutes_asleep,
             s.session_id,
             db_name(s.database_id) as database_name,
             s.host_name,
             s.host_process_id,
             t.text as last_sql,
             s.program_name
        from sys.dm_exec_connections c
        join sys.dm_exec_sessions s
             on c.session_id = s.session_id
       cross apply sys.dm_exec_sql_text(c.most_recent_sql_handle) t
       where s.is_user_process = 1
             and s.status = 'sleeping'
             and db_name(s.database_id) = @database_name
             and s.host_process_id = @host_process_id
             and s.host_name = @host_name
             and datediff(second, s.last_request_end_time, getdate()) > 60
       order by s.last_request_end_time;

    これで、テキストを使用してアプリケーションのコードベースを検索し、データベース接続のリークが発生する可能性のある場所を見つけることができます。

    これらのクエリは、データベース接続リークのトラブルシューティングに役立ち、モニターまたはヘルスチェックの作成にも使用できます。

    使い捨てを処分し、それらの使用法を使用し、それらの漏れを封印してください!

    作者について

    Michael J Swartは、データベース開発とソフトウェアアーキテクチャに焦点を当てた情熱的なデータベースの専門家およびブロガーです。彼は、コミュニティプロジェクトに貢献し、データに関連するあらゆることについて話すことを楽しんでいます。 Michaelは、michaeljswart.comで「DatabaseWhisperer」としてブログを書いています。
    1. SQL Serverエラー111:「…クエリバッチの最初のステートメントである必要があります」

    2. .NETでのMySQLデータベースへの接続

    3. 見落とされたT-SQLジェム

    4. Oracle .Net ManagedDataAccessエラー:タイプ「OracleInternal.Common.ConfigBaseClass」をアセンブリからロードできませんでした