所有死鎖的根本原因都是多個進程對有限資源的競爭性鎖定順序不一致。
-- 進程 A
SELECT * FROM TableA WHERE ... -- 鎖定 TableA
SELECT * FROM TableB WHERE ... -- 等待 TableB
-- 進程 B
SELECT * FROM TableB WHERE ... -- 鎖定 TableB
SELECT * FROM TableA WHERE ... -- 等待 TableA
解決:
-- 進程 A (事務)
SELECT * FROM Table WHERE ID=1 -- 獲得共享鎖 (S)
UPDATE Table SET ... WHERE ID=1 -- 嘗試升級為排他鎖 (X),需等待 B 釋放 S
-- 進程 B (事務)
UPDATE Table SET ... WHERE ID=1 -- 直接獲得排他鎖 (X),但被 A 的 S 鎖阻塞
雙方互相等待對方釋放相容性較低的鎖,形成死鎖。
解決:在進程 A 的 SELECT 中顯式指定更新鎖:
SELECT * FROM Table WITH (UPDLOCK) WHERE ID=1
UPDLOCK 提示讓讀取階段直接獲得更新鎖 (U),避免後續升級時的衝突。
SET LOCK_TIMEOUT 避免無限等待。-- 鎖定特定行 (需搭配適當隔離級別)
SELECT * FROM Table WITH (ROWLOCK, UPDLOCK) WHERE ID = 1
-- 鎖定整張表
SELECT * FROM Table WITH (TABLOCKX, HOLDLOCK)
掌握鎖相容性矩陣與鎖提示,是寫出高併發 SQL Server 應用的關鍵。