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

意図しない副作用–ロックを保持している睡眠セッション

    最近のコンサルティング契約は、アプリケーションからのユーザー要求の処理の遅延を引き起こしていたSQLServer内の問題をブロックすることに焦点を当てていました。発生している問題を掘り下げ始めると、SQL Serverの観点から、問題はエンジン内でロックを保持しているスリープ状態のセッションに関連していることが明らかになりました。これはSQLServerの一般的な動作ではないため、最初に考えたのは、アプリケーションの接続プールのためにリセットされたセッションでトランザクションをアクティブのままにする、ある種のアプリケーション設計の欠陥があることでしたが、これはすぐには証明されませんでした。後でロックが自動的に解放されたため、これが発生するのは遅れただけでした。そのため、さらに掘り下げる必要がありました。

    セッションステータスの理解

    SQL Serverで確認するDMVに応じて、セッションにはいくつかの異なるステータスがあります。スリープ状態とは、エンジンがコマンドを完了し、クライアントとサーバー間のすべての対話が完了し、接続がクライアントからの次のコマンドが来るのを待っていることを意味します。スリープ状態のセッションに開いているトランザクションがある場合、SQL Serverではなく、常にコードに関連しています。オープンに保持されているトランザクションは、いくつかの理由で説明できます。最初の可能性は、XACT_ABORT設定をオンにせず、CSSチームによるこの非常に古い投稿で説明されているように、アプリケーションがクリーンアップを正しく処理せずにタイムアウトする明示的なトランザクションを伴うプロシージャです。

    • 仕組み:スリープ/待機中のコマンドセッションとは

    プロシージャがXACT_ABORT設定を有効にしていた場合、タイムアウト時にトランザクションが自動的に中止され、トランザクションがロールバックされます。 SQL Serverは、ANSI規格の下で実行する必要があることを正確に実行し、実行されたコマンドのACIDプロパティを維持します。タイムアウトはSQLServerに関連するものではなく、.NETクライアントとCommandTimeoutプロパティによって設定されるため、SQLエンジンに関連する動作ではなく、コードに関連するものです。これは、拡張イベントシリーズでもこのブログ投稿で話したのと同じ種類の問題です:

    • 複数のターゲットを使用して孤立したトランザクションをデバッグする

    ただし、この場合、アプリケーションはデータベースへのアクセスにストアドプロシージャを使用せず、すべてのコードはORMによって生成されました。この時点で、調査はSQL Serverから、アプリケーションがORMをどのように使用するか、およびアプリケーションコードベースによってトランザクションが生成される場所に移りました。

    .NETトランザクションについて

    SQL Serverは、セッションでIMPLICIT_TRANSACTIONSセットオプションがオンになっていない限り、自動的にコミットされるトランザクションでデータの変更をラップすることは一般的な知識です。コードのどの部分でもこれがオンになっていないことを確認した後、セッションがスリープ状態になった後に残っているトランザクションは、コードの実行中にどこかで明示的なトランザクションが開かれた結果であると考えるのは非常に安全でした。今では、いつ、どこで、そして最も重要なのは、なぜすぐに閉鎖されなかったのかを理解することだけでした。これは、アプリケーション層コードの内部を探す必要があるいくつかの異なるシナリオの1つにつながります。

    • 操作の周りでTransactionScope()を使用するアプリケーション
    • 接続にSqlTransaction()を登録するアプリケーション
    • コミットされていないトランザクション内の特定の呼び出しを内部的にラップするORMコード

    TransactionScopeのドキュメントでは、これの考えられる原因としてすぐに除外されました。トランザクションスコープの完了に失敗すると、トランザクションが破棄されるときに自動的にロールバックしてトランザクションを中止するため、接続がリセットされてもこれが続く可能性はほとんどありません。同様に、接続プールのために接続がリセットされたときに、コミットされていない場合、SqlTransactionオブジェクトは自動的にロールバックするため、すぐに問題のスターターではなくなりました。これはORMコード生成を残したばかりで、少なくともそれは私が思っていたものでした。非常に一般的なORMの古いバージョンが私の経験からこのタイプの動作を示すのは信じられないほど奇妙なので、さらに掘り下げる必要がありました。

    彼らが使用しているORMのドキュメントには、マルチエンティティアクションが発生すると、トランザクション内で実行されることが明確に記載されています。マルチエンティティアクションは、再帰的な保存またはアプリケーションからデータベースへのエンティティコレクションの保存である可能性があり、開発者はこれらのタイプの操作がコード全体で行われることに同意したため、ORMはトランザクションを使用する必要がありますが、なぜそれらは突然問題になります。

    問題の根源

    この時点で、私たちは一歩下がって、ブロッキングの問題が発生したときに利用可能だったNewRelicやその他の監視ツールを利用して環境全体の全体的なレビューを開始しました。ロックを保持するスリープセッションは、IISアプリケーションサーバーに極端なCPU負荷がかかっている場合にのみ発生することが明らかになりましたが、それだけでは、ロックを解放するトランザクションコミットで見られた遅延を説明するのに十分ではありませんでした。また、アプリケーションサーバーはオーバーコミットされたハイパーバイザーホスト上で実行されている仮想マシンであり、VM管理者から提供された合計値に基づいて、ブロッキングの問題が発生したときに、アプリケーションサーバーのCPU準備完了待機時間が大幅に増加したことも判明しました。

    スリープ状態は、オブジェクトの.SaveEntity呼び出しの完了と、オブジェクトのコード生成コードビハインドの最終コミットとの間にロックを保持しているオープントランザクションで発生します。 VM / Appサーバーにプレッシャーや負荷がかかっている場合、これが遅れてブロックの問題が発生する可能性がありますが、問題はSQL Serverにはなく、トランザクションの範囲内で適切に実行されています。問題は、最終的にはアプリケーション側のコミットポイントの処理の遅延の結果です。ステートメントが完了したタイミングとRPCが完了したイベントを拡張イベントからdatabase_transaction_endイベントのタイミングとともに取得すると、開いている接続でトランザクションを終了するアプリ層からのラウンドトリップ遅延が示されます。この場合、SQL Serverに表示されるものはすべて、過負荷のアプリケーションサーバーと過負荷のVMホストの犠牲になります。 CPU使用率でオーバーコミットされていないホストを使用して、NLBまたはハードウェア負荷分散構成のサーバー間でアプリケーション負荷を移動/分割すると、トランザクションの即時コミットがすばやく復元され、SQLServerでロックを保持しているスリープセッションが削除されます。

    >

    ありふれたブロッキング問題のように見えたものを引き起こしている環境問題のさらにもう1つの例。ブロッキングスレッドがロックをすばやく解放できない理由を調査することは、常に有益です。


    1. WordPressとNginx、MariaDB 10、PHP7をDebian9にインストールします

    2. MySQLのスケーリングソリューション(レプリケーション、クラスタリング)

    3. エラー:インデックス式の関数は、PostgresでIMMUTABLEとマークする必要があります

    4. SQLiteで現在の時刻を取得する方法