web-dev-qa-db-ja.com

アクティブなAzure SQL接続が接続プールの制限を超えています

Azure SQLデータベースのパフォーマンスが大幅に低下することがあるときは、運用環境でこの問題に対処します。テーブルの1つにロックがあることはわかっていますが、これらのロックはデッドロックではなく、長いロックであり、1時間ほどでパフォーマンスは通常に戻ります。これらの長いロックを取得する方法について考えられるすべてのシナリオを見つけようとしています(すべてのクエリは非常に高速で、すべてのパフォーマンスアナライザーが長いロックの原因を示すことができます)。この質問の理由は下の写真です:

enter image description here

Out接続プールの設定では、200接続のみをプールできます。また、ほとんどの場合、データベースとのオープン接続またはプール接続は約10〜20です。その後、突然アクティブな接続の数が増え始め、プールが完全に使用されます。プールされた接続の数は200未満のままですが、sp_who2を使用するアクティブな接続の数は1.5kから2kに達します(4kから5kに達する場合があります)。

Azure Portal監視ツールを使用して同じチャートを作成しました。集計期間は異なりますが、同じ問題が表示されます: enter image description here

使用する接続文字列:

データソース= [サーバー]。データベース.windows.net;初期カタログ= [データベース];永続的なセキュリティ情報= True;ユーザーID = [ユーザー];パスワード= [パスワード]; MultipleActiveResultSets = True;接続タイムアウト= 30;最大プールサイズ= 200;プーリング= True; App = [AppName]

200接続の接続プール制限を考慮に入れると、どのように可能ですか?

pS:定期的なタスク、長時間実行されるクエリ、その他のツールによる処理はありません。データベースへのすべてのアクティブな接続をsp_who2で確認しました。

12
Alexey Strakh

[これは回答というより長いコメントです]

同じデータベースに複数のホストが接続されていますが、各ホストには200接続という同じ制限があります

接続プールは(接続文字列、AppDomain)ごとです。各サーバーには複数のAppDomainがある場合があります。また、各AppDomainには、接続文字列ごとに1つの接続プールがあります。したがって、ここで異なるユーザー/パスワードの組み合わせがある場合、それらは異なる接続プールを生成します。したがって、200を超える接続が可能である理由は本当の謎ではありません。

では、なぜ多くの接続を取得しているのですか?考えられる原因:

接続リーク。

DbContextまたはSqlConnectionの破棄に失敗した場合、その接続はファイナライズされるまでマネージヒープに残り、再利用できません。接続プールが制限に達すると、新しい接続要求は接続が使用可能になるまで30秒待機し、その後失敗します。

このシナリオでは、サーバーでの待機やブロックは表示されません。セッションはすべて待機せずにアイドル状態になります。そして、多くのリクエストはありません

select *
from sys.dm_exec_requests 

セッション待機統計がAzure SQL DBでライブになりましたので、リアルタイムのブロックと待機をより簡単に確認できることに注意してください。

select *
from sys.dm_exec_session_wait_stats

ブロッキング。

着信要求が何らかのトランザクションによってブロックされ始め、新しい要求が開始し続ける場合、新しい要求が新しいセッションを取得し、要求を開始してブロックされるようになると、セッション数が増える可能性があります。ここでは、ブロックされたリクエストがたくさん表示されます

select *
from sys.dm_exec_requests

遅いクエリ。

リソースの可用性(CPU、ディスク、ログ)が原因で、リクエストが終了するまでに長時間話している場合、これを確認できます。ただし、この間はDTUの使用率が低いため、そうなることはほとんどありません。

したがって、次のステップは、これらの接続がサーバー上でアクティブであるかどうかを確認するか、サーバー上でアイドル状態になって接続プールの問題を示唆しているかどうかを確認することです。

Dbcontextオブジェクトをチェックして、それらを正しく使用しているかどうかを確認し、オブジェクトを破棄して接続を接続プールに返すことができます。

まず、コードからdbcontextを作成します。 dbcontextオブジェクトの各作成スコープの周りにusingステートメントがあるかどうかを確認します。何かのようなもの:

using (var context = new xxxContext()) {
    ...
}

これにより、コンテキストが自動的に範囲外になったときにコンテキストが破棄されます。

次に、依存関係注入を使用してdbcontextオブジェクトを注入します。スコープを使用していることを確認してください:

services.AddScoped<xxxContext>(

次に、DIがコンテキストオブジェクトを破棄します。

次に確認できるのは、コミットされていないトランザクションがあるかどうかです。すべてのトランザクションがusingブロック内にあるかどうかを確認します。これにより、スコープ外にあるときにトランザクションがコミットまたはロールバックされます。

2
Peter

問題は「プールの断片化」に関連している可能性があります

プールの断片化は、アプリケーションがプロセスが終了するまで解放されない大量のプールを作成できる多くのWebアプリケーションでの一般的な問題です。これにより、多数の接続が開いたままになり、メモリが消費されるため、パフォーマンスが低下します。

統合セキュリティによるプールの断片化*接続は、接続文字列とユーザーIDに従ってプールされます。したがって、Webサイトで基本認証またはWindows認証を使用し、統合セキュリティログインを使用する場合、ユーザーごとに1つのプールを取得します。これにより、1人のユーザーに対する後続のデータベース要求のパフォーマンスは向上しますが、そのユーザーは他のユーザーが行った接続を利用できません。また、データベースサーバーへのユーザーごとに少なくとも1つの接続が発生します。これは、特定のWebアプリケーションアーキテクチャの副作用であり、開発者はセキュリティと監査の要件と比較検討する必要があります。

ソースhttps://docs.Microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-接続プーリング

1
Shailendra