在講授 SQL Server 鎖與阻塞時,最常被問到的問題:**UPDATE 語句掃描階段為什麼獲取更新鎖(U Lock),而不是共享鎖(S Lock)或直接獲取排他鎖(X Lock)?** 本文從死鎖避免、併發提升、非對稱兼容性三個維度深度解析 U Lock 的必要性與設計精妙。
階段 1:讀取數據(定位行) → 獲取 更新鎖 (U Lock) 階段 2:計算新值(CPU 運算) → 持有 U Lock 階段 3:寫入數據(修改頁) → U Lock 轉換為 排他鎖 (X Lock)
若階段 1 用 S Lock,階段 3 轉 X Lock 會發生什麼?
假設兩個並發 UPDATE 同一張表,不同行,但掃描路徑重疊:
U Lock 解決:U Lock **與 S Lock 兼容,但與 U Lock、X Lock 不兼容**。
若階段 1 直接獲取 X Lock:
U Lock 解決:U Lock **與 S Lock 兼容**。
| 請求 \ 持有 | S (共享) | U (更新) | X (排他) |
|---|---|---|---|
| S | ✓ 兼容 | ✓ 兼容 (SQL Server) | ✗ 阻塞 |
| U | ✓ 兼容 | ✗ 阻塞 | ✗ 阻塞 |
| X | ✗ 阻塞 | ✗ 阻塞 | ✗ 阻塞 |
-- 會話 1:開始更新,停在計算階段(模擬長事務) BEGIN TRAN; UPDATE dbo.BigTable SET Val = Val + 1 WHERE Id = 1; -- 持有 U Lock → 轉 X Lock -- 不提交,保持鎖 -- 會話 2:並發 SELECT 同一行 SELECT * FROM dbo.BigTable WHERE Id = 1; -- 讀已提交:被 X Lock 阻塞 -- 會話 3:並發 UPDATE 不同行 UPDATE dbo.BigTable SET Val = Val + 1 WHERE Id = 2; -- 掃描階段獲取 U Lock,若掃描經過 Id=1 頁會被 U/U 阻塞 -- 觀察 sys.dm_tran_locks SELECT request_session_id, resource_type, resource_description, request_mode, request_status FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID();
更新鎖(U Lock)是 SQL Server 為 **UPDATE/DELETE/MERGE** 的 **掃描-修改** 兩階段模式量身定制的混合鎖:
理解 U Lock,是掌握 SQL Server 併發控制、排查死鎖、設計高併發寫入邏輯的核心鑰匙。