web-dev-qa-db-ja.com

互いにロックしている異なるテーブルからのクエリ(LCK_M_X)

背景:パフォーマンステストのためにJMeterと協力するように雇われましたが、SQLのパフォーマンスにもますます時間を費やしています。私はまだ吸います、そして長い間そうします。私はできる限り説明します。

多くのトランザクションによって更新されるテーブル(NUMERATION)があります。ほとんどの一意の識別子が含まれているため、多くの更新が行われます。

このクエリ

UPDATE Numeration WITH (ROWLOCK) SET NumCounter = NumCounter + 1 WHERE Tid = @p0

...これと関係のないクエリによってロックされていることがあり、その理由はわかりません。

テーブルにトリガーはありません。別のテーブルの一意の識別子を参照する外部キーがあり、すべての関係をマップする方法がわかりませんが、両方のクエリにこの識別子のFKがあると思います

sp_WhoIsActiveおよびsp_BlitzFirstセッションIDとロックのタイプを表示するには(LCK_M_X)。クエリプランは次のとおりです。

https://www.brentozar.com/pastetheplan/?id=r1ebGh7Lr

あなたが私の宿題ではなく私を助けてくれることを願っています。

2
xicotaslb

コミットされていないトランザクション

Dan Guzman が言及しているように、コミットされていないトランザクションを確認し(DBCC OPENTRANを使用するなど)、ブロッキングセッションによって保持されているロックを確認します。クエリがタイムアウトしたか、正しくロールバックされなかった可能性があり、開いているトランザクションとのプールされた接続が再利用された可能性があります。ロックはその「無関係な」クエリのセッションによって保持されていることを覚えておく必要があります。そのセッションでは、識別子の更新が以前に行われ、まだロックされている可能性が高いです( eckes で言及)。

外部キー

Aaron Bertrand 言及された外部キーがブロックの問題に関与している可能性があります。これらのテーブルに定義されている外部キーがあることを確認したので、簡単に説明します。

例として、実行計画の2番目の更新を考えます。

UPDATE Entity WITH (ROWLOCK) 
SET EntityTypeCid = 2, EntityNumber = 49634989, NumberIssued = 1 WHERE Did = @p10

そして、そのステートメントの実行プランのこのサブセット:

screenshot of FK assertion in execution plan

予想どおり、Entityテーブルを更新しているため、その更新を実行する「クラスター化インデックスの更新」演算子があります*。ただし、EntityTypeテーブルへのシークとそれに続くAssertがあり、Entity.EntityTypeCid(この場合は2)に設定されている値にEntityType.Cidの対応する一致)。

つまり、更新クエリのように見えますが、実際にはEntityテーブルのロックのみを取得しますが、 EntityTypeテーブルからのロックも必要です(外部キー制約を検証するため)。そのため、EntityTypeテーブルのロックを取得する他のクエリがこれをブロックする可能性があります。

Numeration.NumCounterの更新時にそのような兆候は見られませんが、他のテーブルがNumCounterを外部キーとして参照している場合、同じことが(逆に)発生している可能性があります。

トリガー

別の可能性としては、テーブルにトリガーが定義されており、他のテーブルへのデータアクセスを実行して、ブロッキングを引き起こしている可能性があります。これらの特定のテーブルにはトリガーが含まれないことをすでに明確にしたので、完全を期すためにこれについて言及します。

長期実行トランザクション

コメントでプロファイラーのスクリーンショットを共有しました:

screenshot of profiler output

次のアクションはすべてトランザクション内で発生するように見えるため、セッションID 423を強調表示しました。

  1. Numerationテーブルが更新されます
  2. NumCounter列が選択されている(おそらくNumerationテーブルから)
  3. 別のステートメントが実行されます-コメントとクエリプランに基づいて、これはEntityテーブルを更新していると思います
  4. 次に、トランザクションがコミットされます

NumerationおよびEntityのロックは、デフォルトのREAD COMMITTED分離レベルを使用する場合、トランザクションの期間中保持されます。これにより、これらの行の読み取りまたは更新を試みる他のセッションがブロックされる可能性があります。

この特定のスクリーンショットでは、そのすべてが非常に迅速に行われました。ただし、3番目のステートメントに予想よりも時間がかかる場合、Numerationのロックも長く保持され、他のスクリーンショットに示されているようなブロックチェーンが作成されます。

screenshot of sp_WhoIsActive blocking chain

多数のセッションがNumerationテーブルへのアクセスを待機していることに注目してください。これらのセッションはすべて、セッション227(Numerationテーブルを更新しています)によってブロックされています。セッション227はセッション222によってブロックされていますが、スクリーンショットには表示されていません。

セッション222が何をしているのかを知らずに、これについて何をすべきかを提案するのは困難です。それが読み取りクエリの場合、READ COMMITTED SNAPSHOT分離(RCSI)などの楽観的分離レベルを使用すると、読者が書き込み者をブロックしないため、運が良いかもしれません。

問題は、より高いレベルのアーキテクチャの問題である可能性があります。このNumerationテーブルは、何らかの方法でスケールアウトする必要があるボトルネックです。


*これはおそらくあなたの質問には関係ありませんが、更新は、更新される列の1つ以上を含むそのテーブルの3つの非クラスター化インデックスも更新します。これはSSMSではそれほど明白ではありませんが、SentryOne Plan Explorerはうまく機能します

2
Josh Darnell