鎖機制與事務隔離等級是 SQL Server 並發控制的核心。本文透過實測範例,詳細解析 NOLOCK、READPAST、ROWLOCK、HOLDLOCK、TABLOCK、TABLOCKX、UPDLOCK 等資料表提示的行為差異,以及四大標準隔離等級與快照隔離的實際運作模式。
-- 會話 1:開啟交易插入資料但不提交 BEGIN TRAN t INSERT INTO Customer SELECT 'a','a' -- 此處未 COMMIT,資料處於未提交狀態
會話 2 執行不同查詢的結果:
| 查詢語句 | 行為 | 結果說明 |
|---|---|---|
SELECT * FROM Customer WITH (NOLOCK) | 不加共享鎖,直接讀取 | 顯示 'a' 'a'(髒讀)。若會話 1 回滾,此資料變為髒資料 |
SELECT * FROM Customer | 預設行為,請求共享鎖 | 被排他鎖阻塞,直到鎖釋放或逾時(可設 SET LOCK_TIMEOUT 1800) |
SELECT * FROM Customer WITH (READPAST) | 略過已鎖定的列/頁 | 顯示 'a' 未提交前的狀態,不鎖定整張表,不會被阻塞 |
-- 會話 3:插入並提交 BEGIN TRAN t INSERT INTO Customer SELECT 'b','b' COMMIT TRAN t
此時即使會話 1 回滾,'b' 仍會保留在資料庫中。
不加任何鎖,允許增刪查改不阻塞:
BEGIN TRAN ttt SELECT * FROM Customer WITH (NOLOCK) WAITFOR DELAY '00:00:20' COMMIT TRAN ttt -- 其他會話可自由操作: INSERT INTO Customer SELECT 'a','b' -- 不等待 DELETE Customer WHERE ID=1 -- 不等待 SELECT * FROM Customer -- 不等待 UPDATE Customer SET Title='aa' WHERE ID=1 -- 不等待
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ BEGIN TRAN ttt SELECT * FROM Customer WITH (ROWLOCK) WHERE ID=17 WAITFOR DELAY '00:00:20' COMMIT TRAN ttt -- 其他會話影響: INSERT INTO Customer SELECT 'a','b' -- 不等待 DELETE Customer WHERE ID=17 -- 等待(鎖衝突) DELETE Customer WHERE ID<>17 -- 不等待 SELECT * FROM Customer -- 不等待 UPDATE Customer SET Title='aa' WHERE ID=17 -- 等待 UPDATE Customer SET Title='aa' WHERE ID<>17 -- 不等待
| 提示 | 其他交易可讀取 | 其他交易可更新/刪除 | 備註 |
|---|---|---|---|
| HOLDLOCK | 是 | 否(等待) | 持有共享鎖至交易結束 |
| TABLOCK | 是 | 否(等待) | 表級共享鎖 |
| TABLOCKX | 否(等待) | 否(等待) | 表級排他鎖,完全獨佔 |
僅對更新操作加鎖,讀取與插入不受影響:
-- 會話 A BEGIN TRAN ttt SELECT * FROM Customer WITH (UPDLOCK) WAITFOR DELAY '00:00:10' COMMIT TRAN ttt -- 其他會話: UPDATE Customer SET Title='aa' WHERE ID=1 -- 等待 10 秒 SELECT * FROM Customer -- 不等待 INSERT INTO Customer SELECT 'a','b' -- 不等待
注意:使用這些提示會覆蓋
SET TRANSACTION ISOLATION LEVEL設定的隔離等級。
允許讀取未提交資料。相當於 NOLOCK。
風險:若來源交易回滾,讀到的資料即為髒資料。
只能讀取已提交資料,避免髒讀。
無法防止:不可重複讀、幻影讀。
阻止其他交易修改正在被讀取的資料,直到讀取交易結束。
防止:髒讀、不可重複讀。
無法防止:幻影讀(新增符合條件的新列)。
使用範圍鎖防止其他交易插入/刪除符合條件的列。
防止:髒讀、不可重複讀、幻影讀。
代價:並發度最低,鎖衝突最多。
為每個交易提供資料快照,讀取不阻塞寫入,寫入不阻塞讀取(MVCC 機制)。
防止:髒讀、不可重複讀、幻影讀。
需求:資料庫啟用 ALLOW_SNAPSHOT_ISOLATION 與 READ_COMMITTED_SNAPSHOT。
NOLOCK 或 READ UNCOMMITTED 可讀取 X 鎖資源。資料修改語句通常同時請求 S 鎖(讀取聯結表)與 X 鎖(修改目標表)。