web-dev-qa-db-ja.com

テーブル間に関係がない場合でもデッドロックが発生する

私はデッドロックについて研究してきましたが、それはさまざまな原因です。この問題に関する例を見つけましたが、2つのDIFFERENTテーブルが関係しているため、デッドロックの原因を正確に理解できませんでした。

まず、接続1でこのクエリを実行します。

Begin Tran

 Update  Purchasing.PurchaseOrderHeader
 Set Freight = Freight * 0.9
 Where PurchaseOrderID = 1255;

もう一度、このクエリを接続2で実行します。

update Purchasing.PurchaseOrderDetail
Set OrderQty = 4
Where ProductID = 448 and
      PurchaseOrderID = 1255

接続1に戻ると、次のクエリを実行します(これによりデッドロックが発生します)。

Begin Transaction

             Update Purchasing.PurchaseOrderDetail
             Set OrderQty = 2
             where ProductID = 448 and
                   PurchaseOrderID = 1255

ご覧のように、トランザクションには2つのテーブルが含まれているため、デッドロックの原因は何ですか?前もって感謝します

3
user156829

2つのDIFFERENTテーブルが関係しているため、デッドロックの原因を正確に理解できませんでした。

デッドロックは、2つ以上のセッションが互いに待機しているときに発生します。デッドロックされたリソースが同じテーブル内の行、異なるテーブル、オブジェクト全体などであるかどうかは関係ありません。

スクリプトを実行してデッドロックを再現し、xml_deadlock_report XEトレースからsystem_health列を抽出して、デッドロックレポートXMLを.xdl拡張子の付いたファイルに保存しました。以下は、SSMSからのグラフィカルビューです。

xml_deadlock_report

ご覧のとおり、SPID 58の犠牲者はPurchasing.PurchaseOrderDetail更新キーロックを待機しており、同じキーのSPID 53排他キーロックによってブロックされていました。 SPID 53はPurchasing.PurchaseOrderHeaderの更新キーロックを待機しており、同じキーで保持されている排他ロックSPID 58によってブロックされていました。

以下は未加工のxml_deadlock_report XMLです。

<deadlock>
  <victim-list>
    <victimProcess id="process1a668c9eca8"/>
  </victim-list>
  <process-list>
    <process id="process1a668c9eca8" taskpriority="0" logused="400" waitresource="KEY: 6:72057594048348160 (4ab5f0d47ad5)" waittime="1316" ownerId="2500187" transactionname="user_transaction" lasttranstarted="2018-07-24T06:02:13.303" XDES="0x1a85b00c490" lockMode="U" schedulerid="3" kpid="2932" status="suspended" spid="58" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-07-24T06:02:31.437" lastbatchcompleted="2018-07-24T06:02:31.433" lastattention="2018-07-24T06:00:22.400" clientapp="Microsoft SQL Server Management Studio - Query" hostname="SQLSERVER" hostpid="15904" loginname="DOMAINNAME\USERNAME" isolationlevel="read committed (2)" xactid="2500187" currentdb="6" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
      <executionStack>
        <frame procname="adhoc" line="1" stmtstart="64" stmtend="278" sqlhandle="0x02000000d0c7f31a30fb1ad425c34357fe8ef6326793e7aa0000000000000000000000000000000000000000">  unknown    </frame>
        <frame procname="adhoc" line="1" stmtstart="26" stmtend="326" sqlhandle="0x02000000b66e4532b6686980baf466a9597258f2464dd4f50000000000000000000000000000000000000000">  unknown    </frame>
      </executionStack>
      <inputbuf>               Update Purchasing.PurchaseOrderDetail               Set OrderQty = 2               where ProductID = 448 and                     PurchaseOrderID = 1255   </inputbuf>
    </process>
    <process id="process1a66883d848" taskpriority="0" logused="9484" waitresource="KEY: 6:72057594048413696 (4bc08edebc6b)" waittime="16176" ownerId="2500193" transactionname="user_transaction" lasttranstarted="2018-07-24T06:02:16.577" XDES="0x1a85e004490" lockMode="U" schedulerid="5" kpid="20416" status="suspended" spid="53" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-07-24T06:02:16.577" lastbatchcompleted="2018-07-24T06:02:16.570" lastattention="2018-07-24T06:01:28.820" clientapp="Microsoft SQL Server Management Studio - Query" hostname="SQLSERVER" hostpid="15904" loginname="DOMAINNAME\USERNAME" isolationlevel="read committed (2)" xactid="2500193" currentdb="6" lockTimeout="4294967295" clientoption1="673319008" clientoption2="390200">
      <executionStack>
        <frame procname="AdventureWorks2014.Purchasing.uPurchaseOrderDetail" line="39" stmtstart="2732" stmtend="3830" sqlhandle="0x0300060004cc856a0ee00a016ba3000000000000000000000000000000000000000000000000000000000000">  UPDATE [Purchasing].[PurchaseOrderHeader]              SET [Purchasing].[PurchaseOrderHeader].[SubTotal] =                   (SELECT SUM([Purchasing].[PurchaseOrderDetail].[LineTotal])                      FROM [Purchasing].[PurchaseOrderDetail]                      WHERE [Purchasing].[PurchaseOrderHeader].[PurchaseOrderID]                           = [Purchasing].[PurchaseOrderDetail].[PurchaseOrderID])              WHERE [Purchasing].[PurchaseOrderHeader].[PurchaseOrderID]                   IN (SELECT inserted.[PurchaseOrderID] FROM inserted    </frame>
        <frame procname="adhoc" line="3" stmtstart="64" stmtend="278" sqlhandle="0x02000000d0c7f31a30fb1ad425c34357fe8ef6326793e7aa0000000000000000000000000000000000000000">  unknown    </frame>
        <frame procname="adhoc" line="3" stmtstart="28" stmtend="250" sqlhandle="0x02000000cac3a70ebbbc20e8360adb397f4986f2ced020140000000000000000000000000000000000000000">  unknown    </frame>
      </executionStack>
      <inputbuf>  Begin Tran    update Purchasing.PurchaseOrderDetail  Set OrderQty = 4  Where ProductID = 448 and        PurchaseOrderID = 1255   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057594048348160" dbid="6" objectname="AdventureWorks2014.Purchasing.PurchaseOrderDetail" indexname="PK_PurchaseOrderDetail_PurchaseOrderID_PurchaseOrderDetailID" id="lock1a7fea46a00" mode="X" associatedObjectId="72057594048348160">
      <owner-list>
        <owner id="process1a66883d848" mode="X"/>
      </owner-list>
      <waiter-list>
        <waiter id="process1a668c9eca8" mode="U" requestType="wait"/>
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594048413696" dbid="6" objectname="AdventureWorks2014.Purchasing.PurchaseOrderHeader" indexname="PK_PurchaseOrderHeader_PurchaseOrderID" id="lock1a815dda900" mode="X" associatedObjectId="72057594048413696">
      <owner-list>
        <owner id="process1a668c9eca8" mode="X"/>
      </owner-list>
      <waiter-list>
        <waiter id="process1a66883d848" mode="U" requestType="wait"/>
      </waiter-list>
    </keylock>
  </resource-list>
</deadlock>

サンプルAdventureWorksデータベースには、このデッドロックの一因となったPurchasing.PurchaseOrderDetailテーブルのUPDATEトリガーが含まれています。トリガーはProduction.TransactionHistoryに挿入し、ProductIDOrderQty、またはUnitPrice列が更新されるたびにPurchasing.PurchaseOrderHeaderPurchasing.PurchaseOrderDetailの両方を更新します。

要約すると、このデッドロックにつながるシーケンスは次のとおりです。

1)接続1はPurchasing.PurchaseOrderHeaderを更新しましたが、コミットしませんでした

2)接続2がPurchasing.PurchaseDetailテーブルを更新し、Purchasing.PurchaseOrderHeaderを更新しようとしたときに、起動されたトリガーがブロックされました

3)接続1はPurchasing.PurchaseDetailを更新しようとしましたが、接続によってブロックされました

4)SQL Serverは接続1をデッドロックの犠牲として選択し、接続2が自動コミットできるようにしました

5
Dan Guzman

あなたの例を繰り返します、私はconnection1を実行してからconnection 2を実行します。connection 2でも完了できません。

両方のテーブルは異なりますがconnection 2はブロックされますPurchaseOrderID is Trusted Foreign Keyのため

Transをロールバックして実験を繰り返します。接続1のクエリは同じです。

接続2クエリ、(接続1とは異なるPurchaseOrderIDを保持していることに注意してください)

update Purchasing.PurchaseOrderDetail
Set OrderQty = 4
Where ProductID = 359 and
      PurchaseOrderID = 2

今度は両方の接続を同様の方法で起動します。今回はBoth connectionが正常に完了します。 PurchaseOrderID値が異なるため。

すべてのトリガーが起動され、TransactionHistoryテーブルでPurchaseOrderID = 2のレコードを表示することもできます

FKPurchaseOrderIDから削除すると、デッドロックは発生しません。

したがって、Transactionが適切に処理されない場合、Trusted FKが原因でデッドロックが発生する可能性があります。

Trusted FKは実行計画で非常に役立ちます。

デッドロックにはいくつかの理由が考えられます。

@グズマン、申し訳ありませんが、私の言語がわかりにくい、またはわかりにくい場合。

この例を参照してください。両方のトリガーを無効にした場合

接続1のクエリは、まだCommittedまたはRollbackでないため、引き続き実行されます

接続2クエリもCommittedまたはRollbackではないため、引き続き実行されます。両方のテーブルは異なります。接続2クエリは自動コミットしません。

デッドロックまたはブロックは、トリガーが原因ではありません。

この例では、上記で説明したように、デッドロックは信頼された外部キーが原因です。

IMO、Trusted FKがあり、親テーブルと子テーブルの両方が更新され、トランザクションが適切に処理されない場合、デッドロックが発生します。

0
KumarHarsh

私はデッドロックについて研究してきましたが、それはさまざまな原因です

1つのみデッドロックの原因があります。

2つのプロセスがそれぞれ1つのリソースの排他制御を持ち、他のプロセスが保持する別のリソースの排他制御が必要です。データベースの世界では、これらのリソースは通常、レコードのロックです。

プロセス1は、テーブルAの行1をロックします。

プロセス2は、表Bの行2をロックします。

トランザクションであるため、コミット[またはロールバック]が発行されるまで、これらのロックの制御を放棄しません。

それぞれがもう一方の行をロックしたいすでに持っているロック済み-プロセス1はテーブルBの行2をロックし、プロセス2はテーブルAの行1をロックしたい。ロックがオンの場合- 異なる行の場合、デッドロックは発生しません。

2つのプロセスは「致命的な抱擁」に入り、そこからはどちらも脱出できません。それぞれが相手のロックが解放されるのを永遠に待ちますが、それは決して起こりません。 DBMSはこの状況を検出し、通常はランダムに一方のプロセスを強制終了し、もう一方のプロセスを完了させます。

関係する2つのテーブル間に論理的な関係がある必要はありません。テーブルが存在し、その中の行が2つのプロセスによって「順序が狂って」ロックされていることだけが必要です。

これが、この状況を正確に回避するために、更新が「一般に」「同じ順序」で適用されることを確認する、慎重なアプリケーション設計が不可欠な理由です。両方のプロセスが同じ順序でテーブルAとテーブルBを更新した場合、デッドロックを完全に回避できます。

0
Phill W.