Sql-Server

使用 OR 運算符刪除時出現死鎖

  • October 14, 2018

當有多個會話執行以下操作時,我遇到了僵局:

  1. 開始交易
  2. 向表 A 插入數據
  3. 從表 A 中刪除數據
  4. 犯罪

同時

他們可能會刪除相同或不同的記錄

我可以用下面的簡化程式碼複製它,表格一開始是空的

第 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 操作,並且只有表的一小部分被鎖定。

引用自:https://dba.stackexchange.com/questions/219918