核心成因:資源競爭
所有死結本質皆為多執行緒對有限資源的循環等待。
類型一:多資源交叉鎖定 (程式邏輯缺陷)
情境
- 使用者 A 鎖定資料表 A → 嘗試鎖定資料表 B
- 使用者 B 鎖定資料表 B → 嘗試鎖定資料表 A
- 雙方互相等待對方釋放 → 死結
解決方案:程式碼層面修正
- 避免同時鎖定多資源:拆分交易、縮小交易範圍
- 固定鎖定順序:全系統統一按資料表名稱/ID 排序後再鎖定,打破循環等待條件
類型二:鎖升級衝突 (隱蔽、高發)
情境
- 使用者 A:
SELECT ... (取得共享鎖 S) → UPDATE ... (嘗試轉換為排他鎖 X)
- 使用者 B:
UPDATE ... (持有排他鎖 X,阻擋 A 的轉換)
- A 持有 S 鎖阻擋 B 繼續,B 持有 X 鎖阻擋 A 升級 → 死結
解決方案:更新鎖 (UPDLOCK) 提示
-- 在 SELECT 時主動請求更新鎖,而非共享鎖
SELECT * FROM table1 WITH (UPDLOCK) WHERE ...
原理:更新鎖 (U) 與共享鎖 (S) 相容,但與排他鎖 (X) 和其他更新鎖 (U) **不相容**。A 先取得 U 鎖,B 後續 UPDATE 需 X 鎖會被阻擋;A 後續 UPDATE 轉換 U→X 時無衝突,順利完成。
補充:其他常見死結誘因
- 索引缺失導致表掃描 → 鎖定大量資源 → 死結機率激增
- 長交易、使用者互動式交易 (等待使用者輸入)
- 隔離層級過高 (SERIALIZABLE) 產生範圍鎖
監控與排查
-- 即時檢視死結
DBCC TRACEON(1222, -1) -- 記錄死結圖到錯誤日誌 (SQL 2000/2005)
-- 或使用 SQL Profiler 事件:Deadlock Graph
最佳實務檢查清單
- 所有多表更新是否遵循固定順序?
- 「先查後改」模式是否加上
UPDLOCK?
- 關鍵查詢是否有適當索引避免表掃描?
- 交易是否盡可能短小?
- 是否設定死結重試邏輯 (客戶端捕捉 1205 錯誤並重試)?