web-dev-qa-db-ja.com

SELECT / INSERTデッドロック

このインスタンスは、SharePoint 2007データベース(SP)をホストします。 SPコンテンツデータベース内の使用率の高い1つのテーブルに対して多数のSELECT/INSERTデッドロックが発生しています。関係するリソースを絞り込みました。両方のプロセスで非クラスター化インデックスのロックが必要です。
INSERTはSELECTリソースでIXロックを必要とし、SELECTはINSERTリソースでSロックを必要とします。デッドロックグラフは、3つのリソースを示しています。1)SELECT(プロデューサー/コンシューマーの並列スレッド)からの2つ、および2)INSERTです。
レビュー用にデッドロックグラフを添付しました。これはマイクロソフトのコードとテーブルの構造であるため、変更を加えることはできません。
しかし、私はMSFT SPサイトで、MAXDOPインスタンスレベルの構成オプションを1に設定することをお勧めします。このインスタンスは他の多くのデータベース/アプリケーション間で共有されているため、この設定を無効にすることはできません。


したがって、私はこれらのSELECTステートメントが並列にならないようにすることを試みました。これは解決策ではなく、トラブルシューティングに役立つ一時的な変更であることがわかっています。そのため、ワークロードが変更されていない(SELECT/INSERTが頻繁に発生する)にもかかわらず、デッドロックがなくなったにもかかわらず、「並列処理のコストしきい値」を標準の25から40に増やしました。私の質問はなぜですか?

SPID 356 INSERTは、非クラスター化インデックスに属するページにIXロックを持っています
SPID 690 SELECT実行ID 0は、同じ非クラスター化インデックスに属するページにSロックを持っています

SPID 356はSPID 690リソースのIXロックを必要としていますが、SPID 690がSPID 690実行ID 0 Sロックによってブロックされているため、それを維持できません
SPID 690実行ID 1はSPID 356リソースのSロックを必要としていますが、SPID 356によってSPID 690実行ID 1がブロックされており、デッドロックが発生しているため取得できません。

実行プランはSkyDriveにあります

完全なデッドロックの詳細はここにあります

誰かが私に理解してくれる理由を教えてくれれば、本当に感謝しています。

EventReceiversテーブル。
Id uniqueidentifier no 16
Name nvarchar no 512
SiteId uniqueidentifier no 16
WebId uniqueidentifier no 16
HostId uniqueidentifier no 16
HostType int no 4
ItemId int no 4
DirName nvarcharいいえ512
LeafName nvarcharいいえ256
int in 4と入力します
SequenceNumber int no 4
Assembly nvarchar no 512
クラスnvarcharいいえ512
Data nvarchar no 512
nvarcharをフィルターしない512
SourceId tContentTypeId no 512
SourceType int no 4
Credential int no 4
ContextType varbinary no 16
ContextEventType varbinary no 16
ContextId varbinary no 16
ContextObjectId varbinary no 16
ContextCollectionId varbinary no 16

index_name index_description index_keys
PRIMARY SiteId、ContextCollectionIdにある非クラスター化のEventReceivers_ByContextCollectionId
EventReceivers_ByContextObjectId非クラスター化がPRIMARY SiteId、ContextObjectIdにあります
EventReceivers_ById非クラスター化、一意はPRIMARY SiteId、Idにあります
EventReceivers_ByTargetクラスター化、一意:PRIMARY SiteId、WebId、HostId、HostType、Type、ContextCollectionId、ContextObjectId、ContextId、ContextType、ContextEventType、SequenceNumber、Assembly、Class
EventReceivers_IdUnique PRIMARY Idにある、クラスター化されていない一意の一意のキー

10
SQLJarHead

一見すると、これは古典的な lookup deadlock のように見えます。このデッドロックパターンに不可欠な要素は次のとおりです。

  • キールックアップでカバーしない非クラスター化インデックスを使用するSELECTクエリ
  • クラスター化インデックスを変更してから非クラスター化インデックスを変更するINSERTクエリ

SELECTは、最初に非クラスター化インデックスにアクセスし、次にクラスター化インデックスにアクセスします。 INSERTは最初にクラスター化インデックスにアクセスし、次に非クラスター化インデックスにアクセスします。もちろん、同じリソースに異なる順序でアクセスして互換性のないロックを取得することは、もちろんデッドロックを「達成する」ための優れた方法です。

この場合、SELECTクエリは次のとおりです。

SELECT query

...そしてINSERTクエリは:

INSERT query

緑で強調表示された非クラスター化インデックスのメンテナンスに注意してください。

パラレルバージョンと非常に異なる場合は、SELECTプランのシリアルバージョンを確認する必要がありますが、Jonathan Kehayiasが彼のガイドで Handling Deadlocks と記しているように、この特定のデッドロックパターンは、タイミングと内部クエリ実行の実装の詳細に非常に敏感です。このタイプのデッドロックは、明白な外部の理由なしにしばしば行き来します。

関連するシステムへのアクセスと適切な権限があれば、最終的に並列計画でデッドロックが発生するが、シリアル計画では発生しない理由を正確に突き止めることができると確信しています(同じ一般的な形状を想定しています)。潜在的な問い合わせ行には、最適化されたネストされたループおよび/またはプリフェッチのチェックが含まれます-どちらも内部的に 分離レベルをエスカレート からREPEATABLE READステートメントの期間中。並列インデックスシーク範囲割り当ての一部の機能が問題の原因である可能性もあります。シリアルプランが利用可能になった場合は、興味を引く可能性があるため、詳細をさらに調査するのに少し時間をかけることがあります。

このタイプのデッドロックの通常の解決策は、インデックスをカバーすることですが、この場合の列の数はそれを非現実的にする可能性があります(さらに、私たちはSharePointでそのようなことを混乱させることはないと私は聞いています)。結局のところ、SharePointを使用する場合のシリアルのみのプランの推奨は、理由があります(それには、必ずしも適切なものではありませんが)。 並列処理のコストのしきい値 の変更によって問題が一時的に修正される場合は、これで問題ありません。長期的には、おそらくリソースガバナーを使用してワークロードを分離し、SharePointの内部クエリが目的のMAXDOP 1動作と他のアプリケーションは並列処理を使用できます。

デッドロックの痕跡に現れる交換の問題は、私にとっては赤いニシンのようです。技術的にツリーに表示されなければならないリソースを所有する独立したスレッドの単なる結果。取引所自体がデッドロックの問題に直接貢献していることを示唆するものは何もありません。

14
Paul White 9

これがクラシック ルックアップデッドロック の場合、リソースリストにはクラスター化インデックスと非クラスター化インデックスの両方が含まれます。通常、SELECTはNCインデックスのSHAREDロックを保持し、CIのSHAREDロックを待ちますが、INSERTはCIのEXCLUSIVEロックを取得して、NCのEXCLUSIVEロックを待ちます。この場合、デッドロックxmlのリソースリストには、これらのオブジェクトの両方がリストされます。

デッドロックグラフにはNCインデックスのみが含まれるため、そのオプションを除外できます。

また、これが ネストされたループ結合とUNORDERED PREFETCH によるデッドロックであった場合、実行プランはUNORDERED PREFETCHアルゴリズムが使用されているかどうかを通知しますが、ここでもそうではありません(以下の更新を参照してください)。

そのため、これは並列プランによる デッドロックであると想定する必要があります。

デッドロックグラフは適切にレンダリングされませんが、デッドロックXMLを見ると、SELECTステートメント(SPID 690)からの2つのスレッドがデッドロックに関係していることがわかります。コンシューマスレッドは、ページ1219645で共有ロックを保持し、ポート801f8ed0(e_waitPipeGetRow)でプロデューサを待機しています。プロデューサスレッドは、ページ1155940で共有ロックを待機しています。

INSERTステートメントは、ページ1155940でIXロックを保持しており、ページ1219645でIXロックを待機しているため、デッドロックが発生します。

SELECTステートメントにシリアルプランを使用すると、複数のページでSHAREDロックを必要としないため、デッドロックが回避されると思います。また、シリアルプランはパラレルプランとほぼ同じになると思います(並列化演算子を除きます)。

[ポールのコメントに基づいて更新]

どうやら計画はOPTIMIZEDネストループアルゴリズムを使用しています

これが、SHAREDロックがステートメントの終わりまで保持される理由を説明しています。パラレルプランと組み合わせてREPEATABLE READを実行すると、シリアルプランよりもデッドロックの影響を受けやすくなります。これは、パラレルプランがインデックスの異なる範囲からロックを取得して保持するのに対し、シリアルプランはより順次的な方法でロックを取得するためです。

6
Roji P Thomas