私はデッドロックについて研究してきましたが、それはさまざまな原因です。この問題に関する例を見つけましたが、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つのテーブルが含まれているため、デッドロックの原因は何ですか?前もって感謝します
2つのDIFFERENTテーブルが関係しているため、デッドロックの原因を正確に理解できませんでした。
デッドロックは、2つ以上のセッションが互いに待機しているときに発生します。デッドロックされたリソースが同じテーブル内の行、異なるテーブル、オブジェクト全体などであるかどうかは関係ありません。
スクリプトを実行してデッドロックを再現し、xml_deadlock_report
XEトレースからsystem_health
列を抽出して、デッドロックレポートXMLを.xdl
拡張子の付いたファイルに保存しました。以下は、SSMSからのグラフィカルビューです。
ご覧のとおり、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
に挿入し、ProductID
、OrderQty
、またはUnitPrice
列が更新されるたびにPurchasing.PurchaseOrderHeader
とPurchasing.PurchaseOrderDetail
の両方を更新します。
要約すると、このデッドロックにつながるシーケンスは次のとおりです。
1)接続1はPurchasing.PurchaseOrderHeader
を更新しましたが、コミットしませんでした
2)接続2がPurchasing.PurchaseDetail
テーブルを更新し、Purchasing.PurchaseOrderHeader
を更新しようとしたときに、起動されたトリガーがブロックされました
3)接続1はPurchasing.PurchaseDetail
を更新しようとしましたが、接続によってブロックされました
4)SQL Serverは接続1をデッドロックの犠牲として選択し、接続2が自動コミットできるようにしました
あなたの例を繰り返します、私はconnection
1を実行してから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のレコードを表示することもできます
FK
をPurchaseOrderID
から削除すると、デッドロックは発生しません。
したがって、Transaction
が適切に処理されない場合、Trusted FK
が原因でデッドロックが発生する可能性があります。
Trusted FKは実行計画で非常に役立ちます。
デッドロックにはいくつかの理由が考えられます。
@グズマン、申し訳ありませんが、私の言語がわかりにくい、またはわかりにくい場合。
この例を参照してください。両方のトリガーを無効にした場合
接続1のクエリは、まだCommitted
またはRollback
でないため、引き続き実行されます
接続2クエリもCommitted
またはRollback
ではないため、引き続き実行されます。両方のテーブルは異なります。接続2クエリは自動コミットしません。
デッドロックまたはブロックは、トリガーが原因ではありません。
この例では、上記で説明したように、デッドロックは信頼された外部キーが原因です。
IMO、Trusted FKがあり、親テーブルと子テーブルの両方が更新され、トランザクションが適切に処理されない場合、デッドロックが発生します。
私はデッドロックについて研究してきましたが、それはさまざまな原因です
1つのみデッドロックの原因があります。
2つのプロセスがそれぞれ1つのリソースの排他制御を持ち、他のプロセスが保持する別のリソースの排他制御が必要です。データベースの世界では、これらのリソースは通常、レコードのロックです。
プロセス1は、テーブルAの行1をロックします。
プロセス2は、表Bの行2をロックします。
トランザクションであるため、コミット[またはロールバック]が発行されるまで、これらのロックの制御を放棄しません。
それぞれがもう一方の行をロックしたいすでに持っているロック済み-プロセス1はテーブルBの行2をロックし、プロセス2はテーブルAの行1をロックしたい。ロックがオンの場合- 異なる行の場合、デッドロックは発生しません。
2つのプロセスは「致命的な抱擁」に入り、そこからはどちらも脱出できません。それぞれが相手のロックが解放されるのを永遠に待ちますが、それは決して起こりません。 DBMSはこの状況を検出し、通常はランダムに一方のプロセスを強制終了し、もう一方のプロセスを完了させます。
関係する2つのテーブル間に論理的な関係がある必要はありません。テーブルが存在し、その中の行が2つのプロセスによって「順序が狂って」ロックされていることだけが必要です。
これが、この状況を正確に回避するために、更新が「一般に」「同じ順序」で適用されることを確認する、慎重なアプリケーション設計が不可欠な理由です。両方のプロセスが同じ順序でテーブルAとテーブルBを更新した場合、デッドロックを完全に回避できます。