多使用者並發環境下,兩筆交易同時 `INSERT` 同一資料表,接著各自 `SELECT COUNT(*)` 檢查唯一性條件。預設隔離等級下,`INSERT` 取得 `IX` 鎖 (意圖排他),`SELECT` 需 `S` 鎖 (共享)。`S` 鎖與 `IX` 鎖相容,但**不同連接持有 `IX` 鎖時,彼此請求 `S` 鎖會被對方的 `IX` 鎖阻擋**,最終觸發死鎖,SQL Server 選擇犧牲其中一個交易回滾。
cn.BeginTrans
cn.Execute "INSERT INTO TableA ..."
Set rs = cn.Execute("SELECT COUNT(*) FROM TableA WHERE ...") -- 這裡死鎖
If rs.RecordCount > 0 Then cn.RollbackTrans Else cn.CommitTrans
cn.BeginTrans
cn.Execute "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
cn.Execute "INSERT INTO TableA ..."
Set rs = cn.Execute("SELECT COUNT(*) FROM TableA WHERE ...")
If rs.RecordCount > 0 Then cn.RollbackTrans Else cn.CommitTrans
cn.Execute "SET TRANSACTION ISOLATION LEVEL READ COMMITTED" -- 還原
SELECT COUNT(*) FROM TableA WITH (UPDLOCK, HOLDLOCK) WHERE ...
-- 開啟死鎖追蹤旗標 (SQL 2000/2005)
DBCC TRACEON(1204, -1) -- 輸出至錯誤記錄檔
DBCC TRACEON(1222, -1) -- XML 格式 (SQL 2005+)
-- 或使用擴充事件 (SQL 2008+)
CREATE EVENT SESSION deadlock_monitor ON SERVER
ADD EVENT sqlserver.xml_deadlock_report
ADD TARGET package0.event_file (SET filename=N'deadlock.xel');
死鎖本質是資源競爭與存取順序不一致。優先從**資料庫約束、單一陳述式、統一存取順序**著手解決;隔離等級調整僅作權宜之計,必須評估髒讀/不可重複讀風險。