三大併發異常定義
| 異常 | 發生情境 | 後果 |
| 髒讀 | 事務 A 讀取事務 B 修改但**未提交**的資料 | 若 B 回滾,A 讀到的資料從未存在(無效值) |
| 不可重複讀 | 事務 A 兩次讀取同一列,期間事務 B **修改並提交**該列 | A 兩次讀到不同值,邏輯判斷失效 |
| 幻讀 | 事務 A 兩次執行相同範圍查詢,期間事務 B **插入/刪除**符合條件的列 | A 兩次結果集筆數不同,出現/消失「幻影」列 |
五大隔離等級對照表
| 隔離等級 | 髒讀 | 不可重複讀 | 幻讀 | 鎖定機制 | 並發度 | 適用場景 |
| READ UNCOMMITTED | ❌ 允許 | ❌ 允許 | ❌ 允許 | 不加共享鎖(Sch-S 除外) | 最高 | 近似即時報表、資料倉庫、可容忍髒資料 |
| READ COMMITTED(預設) | ✅ 防止 | ❌ 允許 | ❌ 允許 | 讀取時加共享鎖,讀完即釋放 | 高 | 一般 OLTP、大多數應用預設選擇 |
| REPEATABLE READ | ✅ 防止 | ✅ 防止 | ❌ 允許 | 讀取時加共享鎖,**持有至交易結束** | 中 | 需一致性快照的報表、庫存扣款前檢查 |
| SERIALIZABLE | ✅ 防止 | ✅ 防止 | ✅ 防止 | 範圍鎖,防止插入/刪除幻影列 | 低 | 財務結帳、庫存配額嚴格控制 |
| SNAPSHOT | ✅ 防止 | ✅ 防止 | ✅ 防止 | **列版本控制(行版本存 tempdb)**,讀不阻塞寫 | 高(讀寫不阻塞) | 高並發讀寫混合、長時間報表查詢 |
SNAPSHOT 隔離的特殊機制
- 啟用資料庫選項:
ALTER DATABASE MyDB SET ALLOW_SNAPSHOT_ISOLATION ON;
- 讀取時建立**交易開始時刻的資料快照**(複製到 tempdb 版本存放區),後續讀取皆基於此快照。
- 寫入衝突偵測:若兩事務修改同一列,後提交者會收到錯誤 3960(更新衝突),需重試。
- 不需範圍鎖即可防止幻讀,因快照固定了可見列集合。
設定隔離等級
-- 會話層級(影響後續交易)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 預設
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
-- 單一查詢層級(Table Hint)
SELECT * FROM Orders WITH (READUNCOMMITTED); -- NOLOCK 同義
SELECT * FROM Orders WITH (SNAPSHOT); -- 需資料庫啟用 SNAPSHOT
-- 檢視目前隔離等級
DBCC USEROPTIONS; -- 尋找 'isolation level' 行
選擇決策樹
- **能容忍髒讀嗎?** → 能 →
READ UNCOMMITTED(極少數場景)。
- **需完全序列化一致性?** → 能 →
SERIALIZABLE(鎖爭用高,慎用)。
- **高並發讀寫、長查詢不想被阻塞?** →
SNAPSHOT(需評估 tempdb 壓力)。
- **一般 OLTP、需防髒讀即可?** →
READ COMMITTED(預設,最平衡)。
- **同一交易多次讀同列需一致?** →
REPEATABLE READ。
READ COMMITTED SNAPSHOT (RCSI) 變體
ALTER DATABASE MyDB SET READ_COMMITTED_SNAPSHOT ON;
啟用後,READ COMMITTED 行為變為**語句級快照**(非交易級),讀取不加共享鎖,防髒讀、不可重複讀、幻讀在**單一陳述式**層級自動防止。這是**現代高並發系統的推薦預設值**,無需修改應用程式碼即可獲得讀寫不阻塞優勢。