死鎖四個必要條件(科夫曼條件)
- 互斥:資源獨佔(鎖本質)。
- 請求並保持:持有鎖的同時請求新鎖。
- 非搶佔:持有的鎖只能主動釋放,不可強制剝奪。
- 循環等待:進程鏈 P1→R1→P2→R2→...→P1。
打破任一條件即可避免死鎖。數據庫層面主要針對條件 2、4 優化。
六大避免策略
1. 統一訪問順序(破循環等待)
所有事務按相同順序訪問表/行(如先 Supplier 後 Part)。
實施:強制所有寫操作走存儲過程,在過程中固定表訪問順序。
2. 避免事務中用戶交互(縮短持鎖時間)
事務中絕不等待用戶輸入(彈窗、確認、外部 API 同步調用)。
異步化:用戶操作 → 寫請求表 → 後台 Worker 處理 → 通知結果。
3. 事務簡短、單批次執行(減少鎖持有時長)
- 拆分大事務:批量更新分批提交(如每 5000 行)。
- 減少網絡往返:將多語句打包在一個批次/存儲過程中執行。
- 避免在事務中執行耗時計算/外部調用。
4. 降低隔離級別(減少共享鎖持有)
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 鎖行為 |
| READ UNCOMMITTED | Y | Y | Y | 無 S 鎖(Sch-S) |
| READ COMMITTED (默認) | N | Y | Y | 語句級 S 鎖,讀完即釋放 |
| REPEATABLE READ | N | N | Y | 事務級 S 鎖,持有到結束 |
| SERIALIZABLE | N | N | N | 範圍鎖,並發最低 |
| READ COMMITTED SNAPSHOT (RCSI) | N | N | N | 行版本控制,讀不阻塞寫 |
建議:啟用 RCSI(ALTER DATABASE SET READ_COMMITTED_SNAPSHOT ON),讀寫不阻塞,大幅降低死鎖概率,代價是 tempdb 版本存儲開銷。
5. 使用綁定連接(協同鎖共享)
sp_getbindtoken / sp_bindsession 讓同一應用的多個連接共享鎖空間,避免自死鎖。
現代替代:MARS (Multiple Active Result Sets) 或連接池複用單連接上下文。
6. SELECT 加 WITH (NOLOCK) / READ UNCOMMITTED(讀寫不阻塞)
SELECT * FROM Orders WITH (NOLOCK) WHERE CustomerID = 123;
效果:不獲取 S 鎖(獲取 Sch-S 穩定性鎖),允許髒讀。
適用:報表/查詢/統計類只讀場景,嚴禁用於計費/庫存/決策關鍵數據。
替代:RCSI 同樣實現讀不阻塞寫,且無髒讀,優先選 RCSI。
死鎖監控與事後分析
- 擴展事件:
CREATE EVENT SESSION deadlock_monitor ON SERVER ADD EVENT sqlserver.xml_deadlock_report ADD TARGET package0.event_file (FILENAME=N'Deadlock.xel');
- 追蹤旗標 1222/1204:
DBCC TRACEON(1222, -1) 記錄死鎖圖到錯誤日志(性能影響較大,慎用)。
- 系統健康會話:
system_health 默認捕獲死鎖,查詢:
SELECT CAST(target_data AS XML) FROM sys.dm_xe_session_targets st JOIN sys.dm_xe_sessions s ON s.address = st.event_session_address WHERE s.name = 'system_health';
死鎖圖讀取關鍵點
victim:被選中犧牲的進程(通常回滾成本最低者)。
resource-list:涉及的鎖資源(KEY/PAGE/OBJECT/HOBT)。
process:每個進程的 inputbuf(執行語句)、executionStack(堆棧)、隔離級別、持有/請求鎖模式。
- 分析重點:鎖模式轉換順序、索引缺失導致表鎖/頁鎖升級、長事務。
- 上一篇文章: SQL Server 數據/日志文件 (.mdf/.ldf) 移動目錄完整操作指南:離線 + 修改元數據 + 物理遷移
- 下一篇文章: SQL Server 游標從入門到實戰:概念、語法、分步示例與薪資加薪業務完整演示
Copyright ©
程式碼之家 All Rights Reserved