web-dev-qa-db-ja.com

行レベルのインデックスロックでデッドロックが発生した理由を理解するのに役立ちます

次のデッドロックxmlがあります

<deadlock>
  <victim-list>
    <victimProcess id="process3340d548c8" />
  </victim-list>
  <process-list>
    <process id="process3340d548c8" taskpriority="0" logused="1676" waitresource="KEY: 5:72057594083016704 (80e6876e1037)" waittime="4843" ownerId="6974726" transactionname="user_transaction" lasttranstarted="2018-05-25T13:52:16.627" XDES="0x330b1b4458" lockMode="U" schedulerid="1" kpid="34260" status="suspended" spid="201" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-25T13:52:16.657" lastbatchcompleted="2018-05-25T13:52:16.657" lastattention="1900-01-01T00:00:00.657" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="6974726" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink_DeleteByAttributeValueIds" queryhash="0xdc817ac17586cee6" queryplanhash="0x8759f1b16359d45e" line="7" stmtstart="340" stmtend="644" sqlhandle="0x03000500f793ca333699da00eba8000001000000000000000000000000000000000000000000000000000000">
DELETE
        AVH
    FROM
        [model].[AttributeValueHyperlink] AVH
    INNER JOIN
        @AttributeValueIdsTable AVT
    ON
        [AVT].EntityId = [AVH].AttributeValueI    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByAttributeValueIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="10" stmtstart="490" stmtend="660" sqlhandle="0x030005006cae724fc899da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValueHyperlink_DeleteByAttributeValueIds]
        @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByModelItemIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="13" stmtstart="732" stmtend="918" sqlhandle="0x03000500def65a51d299da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC model.AttributeValue_DeleteByAttributeValueIds
        @AttributeValueIds = @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Generic_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="41" stmtstart="2062" stmtend="2208" sqlhandle="0x0300050096f1cb432e9cda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValue_DeleteByModelItemIds]      
            @ModelItemIdTabl    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Object_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="25" stmtstart="1088" stmtend="1302" sqlhandle="0x0300050061e52c65069dda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[ModelItem_Generic_Delete] 
        @ObjectIdTable,
        @MarkAsDeleted,
        @DeletedBy, 
        @DeletedO    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 5 Object Id = 1697441121]   </inputbuf>
    </process>
    <process id="process3330f29088" taskpriority="0" logused="1744" waitresource="KEY: 5:72057594083016704 (5b32eda0fe69)" waittime="4575" ownerId="6948390" transactionname="user_transaction" lasttranstarted="2018-05-25T13:52:14.370" XDES="0x331cb2c458" lockMode="U" schedulerid="2" kpid="29596" status="suspended" spid="181" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-25T13:52:14.403" lastbatchcompleted="2018-05-25T13:52:14.390" lastattention="1900-01-01T00:00:00.390" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="6948390" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink_DeleteByAttributeValueIds" queryhash="0xdc817ac17586cee6" queryplanhash="0x8759f1b16359d45e" line="7" stmtstart="340" stmtend="644" sqlhandle="0x03000500f793ca333699da00eba8000001000000000000000000000000000000000000000000000000000000">
DELETE
        AVH
    FROM
        [model].[AttributeValueHyperlink] AVH
    INNER JOIN
        @AttributeValueIdsTable AVT
    ON
        [AVT].EntityId = [AVH].AttributeValueI    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByAttributeValueIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="10" stmtstart="490" stmtend="660" sqlhandle="0x030005006cae724fc899da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValueHyperlink_DeleteByAttributeValueIds]
        @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByModelItemIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="13" stmtstart="732" stmtend="918" sqlhandle="0x03000500def65a51d299da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC model.AttributeValue_DeleteByAttributeValueIds
        @AttributeValueIds = @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Generic_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="41" stmtstart="2062" stmtend="2208" sqlhandle="0x0300050096f1cb432e9cda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValue_DeleteByModelItemIds]      
            @ModelItemIdTabl    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Relationship_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="23" stmtstart="1078" stmtend="1302" sqlhandle="0x030005000d989e704f9dda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[ModelItem_Generic_Delete]
        @RelationshipIdTable,
        @MarkAsDeleted,
        @DeletedBy, 
        @DeletedO    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 5 Object Id = 1889441805]   </inputbuf>
    </process>
    <process id="process330f11fc28" taskpriority="0" logused="32948" waitresource="KEY: 5:72057594083016704 (80e6876e1037)" waittime="2558" ownerId="6941127" transactionname="user_transaction" lasttranstarted="2018-05-25T13:52:13.970" XDES="0x33199a4458" lockMode="U" schedulerid="2" kpid="91236" status="suspended" spid="193" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-25T13:52:21.987" lastbatchcompleted="2018-05-25T13:52:21.983" lastattention="1900-01-01T00:00:00.983" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="6941127" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink_DeleteByAttributeValueIds" queryhash="0xdc817ac17586cee6" queryplanhash="0x8759f1b16359d45e" line="7" stmtstart="340" stmtend="644" sqlhandle="0x03000500f793ca333699da00eba8000001000000000000000000000000000000000000000000000000000000">
DELETE
        AVH
    FROM
        [model].[AttributeValueHyperlink] AVH
    INNER JOIN
        @AttributeValueIdsTable AVT
    ON
        [AVT].EntityId = [AVH].AttributeValueI    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByAttributeValueIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="10" stmtstart="490" stmtend="660" sqlhandle="0x030005006cae724fc899da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValueHyperlink_DeleteByAttributeValueIds]
        @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValue_DeleteByModelItemIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="13" stmtstart="732" stmtend="918" sqlhandle="0x03000500def65a51d299da00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC model.AttributeValue_DeleteByAttributeValueIds
        @AttributeValueIds = @AttributeValueId    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Generic_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="41" stmtstart="2062" stmtend="2208" sqlhandle="0x0300050096f1cb432e9cda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[AttributeValue_DeleteByModelItemIds]      
            @ModelItemIdTabl    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Relationship_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="23" stmtstart="1078" stmtend="1302" sqlhandle="0x030005000d989e704f9dda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[ModelItem_Generic_Delete]
        @RelationshipIdTable,
        @MarkAsDeleted,
        @DeletedBy, 
        @DeletedO    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.RelationshipPair_DeleteByModelItemIds" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="21" stmtstart="1252" stmtend="1462" sqlhandle="0x03000500bc1cd015159eda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[ModelItem_Relationship_Delete]
        @RelationshipIds,
        0,
        @DeletedBy,
        @DeletedOn,    </frame>
        <frame procname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.ModelItem_Object_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="20" stmtstart="878" stmtend="1076" sqlhandle="0x0300050061e52c65069dda00eba8000001000000000000000000000000000000000000000000000000000000">
EXEC [model].[RelationshipPair_DeleteByModelItemIds]
        @ObjectIdTable,
        @DeletedBy,
        @DeletedO    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 5 Object Id = 1697441121]   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057594083016704" dbid="5" objectname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink" indexname="IX_AttributeValueHyperlink_AttributeValueId" id="lock3320f42880" mode="U" associatedObjectId="72057594083016704">
      <owner-list>
        <owner id="process3330f29088" mode="U" />
      </owner-list>
      <waiter-list>
        <waiter id="process3340d548c8" mode="U" requestType="wait" />
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594083016704" dbid="5" objectname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink" indexname="IX_AttributeValueHyperlink_AttributeValueId" id="lock3320f4fb00" mode="X" associatedObjectId="72057594083016704">
      <owner-list>
        <owner id="process330f11fc28" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process3330f29088" mode="U" requestType="wait" />
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594083016704" dbid="5" objectname="7b7e4b64-e8dd-4a72-8f98-447678798791.model.AttributeValueHyperlink" indexname="IX_AttributeValueHyperlink_AttributeValueId" id="lock3320f42880" mode="U" associatedObjectId="72057594083016704">
      <owner-list>
        <owner id="process3340d548c8" mode="U" requestType="wait" />
      </owner-list>
      <waiter-list>
        <waiter id="process330f11fc28" mode="U" requestType="wait" />
      </waiter-list>
    </keylock>
  </resource-list>
</deadlock>

私が理解できる限り、IX_AttributeValueHyperlink_AttributeValueIdインデックスには3つの行レベルのロックがあります。それらのいくつか(process330f11fc28)がこのインデックスにXロックを持っている理由はわかりませんが、他のロックにはありません。

また、この削除の実行プランは次のようになります

execution plan

なぜデッドロックが起こっているのか分かりません。すべて問題ないようです。

ちなみにこれはsql Azureデータベース上にあるため、RCSI分離レベルを使用していますが、トランザクションは(C#レイヤーで)コミットされた読み取りを使用するように設定されています。

7
Umair

なぜデッドロックが起こっているのか分かりません。

この実行プランの場合、 each row の削除に関連するロック操作のシーケンスは次のとおりです。

  1. Uロック非クラスター化インデックス(インデックスシークで取得)
  2. Uロッククラスタ化インデックス(削除演算子で取得)
  3. Xロッククラスタ化インデックス(削除演算子で)
  4. X非クラスター化インデックスをロック(削除演算子で)

理由はわかりません... process330f11fc28にはこのインデックスにXロックがありますが、他にはありません。

プランにはブロッキング演算子がないため、単純なパイプラインです(大まかに言って、各行は次の行が処理される前にパイプラインの最後に到達します)。

デッドロックが発生すると、1つのプロセス(セッション193)で非クラスター化インデックス行にXロックが設定されました(上記の最後のステップ)。セッション181および201は最初のステップでブロックされ、セッション193が排他的にロックした同じ非クラスター化インデックス行セッションで互換性のないUロックを取得しようとしました。

細かい説明が多少入りますので、予めご了承ください。

内部更新ロック

非クラスター化インデックスの更新ロックは、一般的なタイプの変換デッドロックを回避するために、エンジンによって自動的に取得されます。 2つのプロセスが同じリソースでSロックを取得し、両方がXへの変換を試みます。お互いがSXに変換するのを防ぐため、デッドロックが発生します。

UUと互換性がありますが、別のSと互換性がないため、Uロックを取得すると、これが防止されます。当然、Sロックは通常RCSIの下では取得されませんが、これらのUロックは取得されます。これにより、古いバージョンの行を更新しようとすることが回避されます。

自動Uロックは、更新操作に行ロケータを提供するテーブルのインスタンスに対してのみ、RCSIの下で行われます。クエリ内の他のテーブル(更新ターゲットへの追加の参照を含む)は、引き続き行のバージョン管理を使用します。

これらの自動Uロックは、通常の更新ロック(UPDLOCKヒントで取得される場合がある)とは異なる有効期間を持っています。通常のUロックは、transactionの最後まで保持されます。内部Uロックはstatementの最後まで保持されますが、例外としてsame plan operator /ロックを取得したは、行が更新に適格でないと推定できます。ロックは即時に解放されますです。

私の記事 Read Committed Snapshot Isolationの下のデータ変更 を参照してください。

循環デッドロック

この自動Uロックは、サイクリックデッドロックからの保護を提供しません。トランザクション内でリソースAとリソースBを変更する2つのトランザクションは、逆の順序でデッドロックすることが保証されています。

  1. トランザクションT1は行R1を変更します(ただし、コミットも中止もしません)。
  2. トランザクションT2は行R2を変更します(ただし、コミットも中止もしません)。
  3. T1は行R2とブロックをロックしようとします(T2には互換性のないロックがあります)。
  4. T2は行R1とブロックをロックしようとします(T1には互換性のないロックがあります)。
  5. デッドロック。

上記の「変更」には、挿入、更新、削除などが含まれます。

特定のデッドロック

質問の例は、このテーマのバリエーションです。ここで、

  • セッション193は行R1を削除し、その行にXを保持しています
  • セッション193は行R2でUの取得を待機しています
  • セッション181はR2でUロックを所有しています
  • セッション181はR1でUを取得するために待機しています
  • デッドロック

(セッション201も行R2でUを取得するために待機していますが、無害な傍観者です。)

明確にするために:上記の正確なデッドロックシーケンスは、質問に示されている正確な実行プランでは発生しません。セッション181は、R2でUを保持できず、R1でUを要求できなくなりました。これは、ブロッキングオペレーターがないため、および/または非クラスター化Uロック。インデックスシークによって検出されたUロックされた行は、次のシーク行が処理される前にXに変換されることが保証されています。

それでも、それが今の声明の計画であるという理由だけで、それがデッドロックが発生したときの計画であったことを意味するものではありません。たとえば、ステートメントレベルの再コンパイルが発生すると、SQL Serverはテーブル変数のカーディナリティを確認できます。これは、代わりにハッシュ結合計画につながる可能性があります。

ハッシュ結合計画

ハッシュ結合プランでは、テーブル変数の行を使用してハッシュテーブルを作成します。これが完了すると、SQL ServerはAttributeValueHyperlinkから行の読み取りを開始し、インデックススキャンによって発行された各行に対してUロックを取得できます(現在、検索するものはありません)。

ハッシュ結合では、各プローブ側の行が結合述語に対して評価されます。一致が見つかった場合、行はクラスター化インデックス削除演算子に進み、クラスター化されたUX、および非クラスター化Xロックが検索と削除の一部として取得されます現在の行に対応するエントリ。

ただし、行がハッシュ結合でしない結合しない場合、Uロックは解放されません。結合されていない行のUロックは、現在のステートメントが終了したときにすべて解放されるまで蓄積され続けます。これは単に、1つの演算子(非クラスター化インデックススキャン)でUロックが取得された結果ですが、別の演算子(ハッシュ結合)での適格性がテストされています。

とにかく、複数のUロックは、報告されたデッドロックを可能にします。

デッドロックを回避する

もちろん、同じネストされたループの計画は、同じデータを処理するときにデッドロックになる可能性もあります(ロックは、より明らかに循環型デッドロックになります)。デッドロックを回避するには、入力セットが互いに素であるか、各セットの行が厳密に同じ順序で処理される(同じ方法でソートされ、実行プランによって同じ順序で処理される)必要があります。

10
Paul White 9

各セッションは、セカンダリインデックスの値に基づいて、同じテーブルから複数の行を削除しています。複数のセッションで同じ行を削除しようとしない場合でも、セッションはセカンダリインデックスを使用して削除する行を検索し、Uロックで読み取り、各行のXロックに変換します。これにより、デッドロックが発生する可能性があります。

Uロックなしで削除する行を検索し、それらを別のステートメントで削除することで、これを回避できます。最初のクエリで見つかった行の一部は、2番目のクエリを試行するまでに削除される場合があります。しかし、あなたはおそらくそれを気にしません。

だから次のようなもの:

     declare @ids_to_delete table(id int)

     insert into @ids_to_delete(id)
     select id
     from [model].[AttributeValueHyperlink] AVH
     INNER JOIN @AttributeValueIdsTable AVT
     ON
        [AVT].EntityId = [AVH].AttributeValueI 

     delete from  [model].[AttributeValueHyperlink] 
     where id in (select id from @ids_to_delete)