Sql-Server
使用 OR 運算符刪除時出現死鎖
當有多個會話執行以下操作時,我遇到了僵局:
- 開始交易
- 向表 A 插入數據
- 從表 A 中刪除數據
- 犯罪
同時
他們可能會刪除相同或不同的記錄
我可以用下面的簡化程式碼複製它,表格一開始是空的
第 1 節:
BEGIN TRANSACTION DECLARE @component_id numeric = 1 DECLARE @relative_component_id numeric = 0 INSERT INTO component_lock(area_id, component_id, generic_id, operator_id, session_id, lock_date, lock_type) VALUES(1, @component_id, 1, 1, 1, '1/1/2018', 1) WAITFOR DELAY '00:00:05' DELETE FROM component_lock WHERE area_id = 1 AND (component_id = @component_id OR component_id = @relative_component_id) AND operator_id = 1 AND generic_id = 1 AND session_id = 1 COMMIT
第 2 節
BEGIN TRANSACTION DECLARE @component_id numeric = 2 DECLARE @relative_component_id numeric = 0 INSERT INTO component_lock(area_id, component_id, generic_id, operator_id, session_id, lock_date, lock_type) VALUES(1, @component_id, 1, 1, 1, '1/1/2018', 1) WAITFOR DELAY '00:00:05' DELETE FROM component_lock WHERE area_id = 1 AND (component_id = @component_id OR component_id = @relative_component_id) AND operator_id = 1 AND generic_id = 1 AND session_id = 1 COMMIT
主鍵是 (area_id, component_id, generic_id)
外鍵是(operator_id)
component_id 和 operator_id 上有 2 個索引(非集群)
從未被選為死鎖受害者的會話中刪除查詢的實際執行計劃 https://www.brentozar.com/pastetheplan/?id=ryyPArbi7
如果我刪除 OR 運算符並將其更改為 2 個刪除查詢,則不會發生死鎖
DELETE FROM component_lock WHERE area_id = 1 AND (component_id = @component_id) AND operator_id = 1 AND generic_id = 1 AND session_id = 1 DELETE FROM component_lock WHERE area_id = 1 AND (component_id = @relative_component_id) AND operator_id = 1 AND generic_id = 1 AND session_id = 1
實際執行計劃 https://www.brentozar.com/pastetheplan/?id=Sy9ACrbsm
任何人都可以向我解釋為什麼有區別嗎?
問題不在於有多少刪除操作,而在於它如何找到要刪除的行。
如果您查看查詢計劃,您幾乎肯定會看到您使用 OR 進行的刪除操作正在掃描整個表。這將取出鎖以確保行在尋找要刪除的行時不會移動。同時,由於表和其他索引得到維護,您的插入操作將取出鎖。如果沒有 OR,您將獲得 Seek 操作,並且只有表的一小部分被鎖定。