死鎖指兩個或多個執行緒在爭奪資源時互相等待,若無外力干預將永遠無法推進。資料庫場景中:
CREATE PROCEDURE sp_who_lock
AS
BEGIN
DECLARE @spid int, @bl int, @intCount int, @intCounter int
CREATE TABLE #tmp_lock_who (id int identity(1,1), spid smallint, bl smallint)
INSERT INTO #tmp_lock_who(spid, bl)
SELECT 0, blocked FROM (SELECT * FROM sysprocesses WHERE blocked > 0) a
WHERE NOT EXISTS (SELECT * FROM (SELECT * FROM sysprocesses WHERE blocked > 0) b WHERE a.blocked = spid)
UNION SELECT spid, blocked FROM sysprocesses WHERE blocked > 0
SELECT @intCount = COUNT(*) FROM #tmp_lock_who
IF @intCount = 0
SELECT '目前無阻塞與死鎖' AS message
ELSE
BEGIN
SET @intCounter = 1
WHILE @intCounter <= @intCount
BEGIN
SELECT @spid = spid, @bl = bl FROM #tmp_lock_who WHERE id = @intCounter
IF @spid = 0
SELECT '引發死鎖進程: ' + CAST(@bl AS varchar(10)) + ',其 SQL:'
ELSE
SELECT '進程 ' + CAST(@spid AS varchar(10)) + ' 被 ' + CAST(@bl AS varchar(10)) + ' 阻塞,SQL:'
DBCC INPUTBUFFER(@bl) -- 顯示阻塞者正在執行的語句
SET @intCounter = @intCounter + 1
END
END
DROP TABLE #tmp_lock_who
RETURN 0
END
執行 EXEC sp_who_lock 即可即時捕捉阻塞鏈與問題 SQL。
所有並發事務按相同順序訪問物件(表/頁/列)。例:所有程序先鎖表 A 再鎖表 B,絕不出現反序。
批次處理速度遠快於人工回應。事務等待輸入會長時間持鎖,阻塞其他事務,降低吞吐量。
READ COMMITTED (預設) → 考慮 READ UNCOMMITTED (NOLOCK) 或啟用 快照隔離 (Snapshot Isolation) (SQL 2005+)同一應用程式的多連接共享鎖,避免自我阻塞。
| # | 建議 | 說明 |
|---|---|---|
| 1 | 熱點表加叢集索引 | 減少頁分裂與鎖升級 |
| 2 | 避免單語句大量 INSERT/UPDATE | 分批處理,減少鎖持有範圍與時間 |
| 3 | UPDATE/DELETE 必須走索引 | 無索引 → 全表掃描 → 大量鎖 → 死鎖溫床 |
| 4 | 嵌套事務謹慎提交/回滾 | 避免內外層邏輯衝突 |
| 5 | 允許髒讀的表加 NOLOCK | SELECT * FROM tableA (NOLOCK) 移除共享鎖 |
| 6 | 查詢操作前置,寫操作後置 | 縮短排他鎖持有窗口 |
| 7 | 使用 TRY...CATCH 確保異常回滾 | 避免異常中斷導致事務懸掛 |
SQL Server 死鎖監視器定期偵測,選擇「回滾成本最低」的犧牲者,拋出錯誤 1205。應用層需捕捉 1205 並實施重試邏輯(指數退避)。
死鎖無法 100% 避免,但可透過「統一鎖順序、縮短事務、降低隔離、補強索引、監控告警」將發生率壓至極低。架構層面引入快照隔離是現代 SQL Server 解決讀寫死鎖的銀彈方案。