所有死鎖的根本原因只有一個:**資源競爭**。本文歸納兩大典型死鎖模式,並給出針對性解決方案,核心在於「統一順序」與「UPDLOCK 避免共享鎖升級」。
此類死鎖純屬應用邏輯缺陷,唯有重構代碼徹底解決。
SELECT * FROM T WHERE id=1 (取得共享鎖 S) → UPDATE T SET ... WHERE id=1 (嘗試升級排他鎖 X)UPDATE T SET ... WHERE id=1 (持有 X 鎖)結果:A 持有 S 鎖等待 B 釋放 X 鎖;B 持有 X 鎖等待 A 釋放 S 鎖 → 死鎖。此模式在中大型專案極常見,且不易在靜態代碼審查中發現。
-- 方案 1:查詢提示 (推薦,影響範圍最小)
SELECT * FROM Table1 WITH (UPDLOCK, HOLDLOCK) WHERE ...;
-- 方案 2:交易隔離層級 (影響整個交易)
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 仍用共享鎖,不解決升級問題
-- 僅 UPDLOCK 能在讀取階段取得 U 鎖,U 鎖與 S 相容、與 U/X 不相容,打破循環等待
| 措施 | 效果 |
|---|---|
| 啟用 READ_COMMITTED_SNAPSHOT | 資料庫級:讀取使用版本存放,完全不加 S 鎖,徹底消除讀寫死鎖 |
| 統一存取順序 (表/索引/主鍵) | 應用級:打破循環等待必要條件 |
| 縮短交易、避免用戶互動在交易中 | 減少持鎖時間窗口 |
| 適當索引覆蓋 WHERE/JOIN 條件 | 縮小掃描範圍,降低鎖升級機率 |
| 應用層捕捉 1205 並指數退避重試 | 容錯處理,提升用戶體驗 |
-- 即時查看封鎖鏈
SELECT
blocking_session_id AS BlockerSPID,
session_id AS VictimSPID,
wait_type,
wait_time_ms,
resource_description
FROM sys.dm_os_waiting_tasks
WHERE blocking_session_id > 0;
-- 查看死鎖受害者最近 SQL (需啟用 trace flag 1222 或 Extended Events)
-- 建議部署 Extended Events session 捕捉 xml_deadlock_report
掌握兩大模式與 UPDLOCK 核心技巧,配合快照隔離與架構規範,即能將死鎖風險降至最低。