首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 鎖定機制實戰:並發 INSERT/SELECT 封鎖與死鎖的三種解決方案

     編輯:關於SqlServer

案例:雙重 INSERT 觸發的鍵範圍鎖衝突

場景:兩用戶同時新增資料,程式邏輯:
1. `BEGIN TRAN` → 2. `INSERT INTO TableA ...` → 3. `SELECT COUNT(*) FROM TableA WHERE ...` 檢查唯一性 → 4. `COMMIT/ROLLBACK`

鎖衝突分析

  • Step 2 `INSERT`:請求 IX (Intent Exclusive) 鎖 於 TableA。IX 鎖相容,兩連線均取得成功。
  • Step 3 `SELECT`:請求 S (Shared) 鎖 於 TableA。S 鎖與 他人持有的 IX 鎖不相容。
  • 連線 A 先執行 SELECT,因連線 B 持有 IX 鎖 → 等待 (Blocking)。
  • 連線 B 隨後執行 SELECT,同樣等待連線 A 的 IX 鎖。
  • 死鎖偵測器介入,選擇犧牲者 (通常成本較低者) → Deadlock Victim (Error 1205),交易回滾。

三種解決方案比較

方案核心機制優點缺點/風險
1. 降低隔離等級
READ UNCOMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED (或 WITH (NOLOCK))
SELECT 讀取未認可資料 (Dirty Read),不請求 S 鎖。
零封鎖、效能最佳、程式碼改動最小。 讀到臟資料 (對方可能 ROLLBACK);唯一性檢查失效 (讀不到對方剛 INSERT 尚未認可的列)。不適合業務關鍵驗證。
2. 表層鎖提示
TABLOCK / TABLOCKX
INSERT INTO TableA WITH (TABLOCK) ...
INSERT 直接請求 表級 X 鎖 (或 S 鎖),序列化所有存取。
完全避免死鎖、邏輯正確 (可讀到對方認可前的資料若配合 HOLDLOCK)。 並發度極低,大表 INSERT 期間全表封鎖,嚴重影響吞吐量。僅適用小表/低併發。
3. 應用層鎖 / 虛設資源鎖
sp_getapplock
邏輯上序列化「檢查並插入」關鍵區:
EXEC sp_getapplock @Resource='TableA_Insert', @LockMode='Exclusive', @LockOwner='Transaction';
... INSERT + SELECT ...
EXEC sp_releaseapplock @Resource='TableA_Insert', @LockOwner='Transaction';
粒度可控 (不鎖全表)、不影響其他無關查詢、支援逾時控制、跨程式協調。 需修改程式碼包裝關鍵區;開發者需理解應用鎖語意;若交易長仍阻塞後續請求。

最佳實踐建議

  1. 資料庫層面強制唯一性:建立 UNIQUE CONSTRAINT 或 UNIQUE INDEX。移除應用層 `SELECT COUNT(*)` 檢查,直接 `INSERT` 捕捉錯誤 2627/2601 回滾。此為根治方案,無鎖爭用、效能最佳、邏輯正確。
  2. 若業務邏輯極其複雜無法單純靠約束,採用 方案 3 (sp_getapplock) 實現應用層序列化。
  3. 避免方案 1 (NOLOCK) 用於寫入前驗證,避免方案 2 (TABLOCK) 用於高併發核心表。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved