想像一個跨國飯店集團,透過中央資料庫追蹤各分店的番茄醬庫存。當總部調撥 50 瓶從 A 店到 B 店時,系統必須同步完成「A 店扣減 50 瓶」與「B 店新增 50 瓶」兩個動作。若程式只執行前者卻因斷電中斷,總庫存數便會出錯——這就是典型的數據不一致。
在多用戶環境下,同時存取同筆資料更易引發衝突。DB2 內建三大機制保障一致性:事務、隔離級別、鎖。本文將逐層剖析這些核心概念,助你掌握 730 認證考試重點。
事務(Transaction),又稱工作單元,是將一或多個 SQL 操作封裝成單一可復原序列。其邊界由啟動與終止(COMMIT/ROLLBACK)定義:
無論成功或失敗,DB2 保證事務結束時資料庫處於一致狀態。但開發者仍需確保 SQL 邏輯本身能產生一致結果——資料庫無法自動修正「邏輯上錯誤但語法正確」的操作序列。
CONNECT TO MY_DB
CREATE TABLE DEPARTMENT (DEPT_ID INTEGER NOT NULL, DEPT_NAME VARCHAR(20))
INSERT INTO DEPARTMENT VALUES(100, 'PAYROLL')
INSERT INTO DEPARTMENT VALUES(200, 'ACCOUNTING')
COMMIT
INSERT INTO DEPARTMENT VALUES(300, 'SALES')
ROLLBACK
INSERT INTO DEPARTMENT VALUES(500, 'MARKETING')
COMMIT
最終 DEPARTMENT 表僅保留 100、200、500 三筆記錄。第一個 COMMIT 固化前兩筆 INSERT,ROLLBACK 取消第三筆,第二個 COMMIT 確認第四筆。
關鍵特性:未提交的變更對其他用戶不可見(除非使用 Uncommitted Read);一旦提交,便永久可見且不可回滾。
若事務執行中途系統崩潰,DB2 會自動回滾所有未提交變更,恢復一致性(前提是事務啟動時資料庫即一致)。下圖對比成功與失敗事務的效果:
多用戶並行存取時,可能發生:
| 現象 | 說明 | DB2 預設行為 |
|---|---|---|
| 丟失更新 | 兩事務讀取同一資料並嘗試更新,後者覆蓋前者 | DB2 架構設計上不允許發生 |
| 髒讀 | 讀取未提交的資料,後來該事務回滾 | 可透過隔離級別防止 |
| 不可重複讀 | 同一事務兩次讀取同一行,得到不同值(因他人已提交修改/刪除) | 可透過隔離級別防止 |
| 幻像讀 | 重新執行查詢時,發現新增符合條件的行(他人已提交 INSERT) | 可透過隔離級別防止 |
隔離級別決定「事務 A 存取資料時,如何鎖定/隔離事務 B」。嚴格度由高到低:
旅館預訂例子:客人掃描可用房間清單期間,經理無法修改該清單內房間價格,其他客人也無法對這些房間訂房/取消,直到該事務結束。但未被掃描的房間不受影響。
旅館例子:客人查詢可用房間後,經理可修改清單外房價;其他客人可訂房/取消。若客人重跑查詢,可能看到新房間或價格變動。
旅館例子:客人逐筆瀏覽房間詳情時,僅「當前正看的那間」被鎖定;換下一間時,前一間立即釋放鎖定。
旅館例子:客人看到的清單可能包含「正被他人訂房中」的房間;經理可隨時改價,不受任何查詢阻擋。
依據不可接受的現象反推:
| 應用類型 | 指定方式 |
|---|---|
| 嵌入式 SQL | PRECOMPILE/BIND 時用 ISOLATION 選項 |
| ODBC/CLI | 執行時呼叫 SQLSetConnectAttr(SQL_ATTR_TXN_ISOLATION) 或設 db2cli.ini 的 TXNISOLATION |
| JDBC/SQLJ | 執行時呼叫 setTransactionIsolation() |
| CLP 腳本 | 連線前執行 CHANGE ISOLATION 命令 |
| 單一查詢 | SELECT ... WITH [RR|RS|CS|UR] 子句(DB2 UDB 8.1+) |
最後一種「查詢級指定」極具彈性:主程式用 CS 追求並發,關鍵報表查詢單獨加 WITH RR 確保一致性。
| 模式 | 適用對象 | 核心語意 |
|---|---|---|
| IN (Intent None) | 表空間、表 | 可讀(含未提交),不加行鎖,他人可讀寫 |
| IS (Intent Share) | 表空間、表 | 可讀,不加行鎖,他人可讀寫;讀取行時加 S 鎖 |
| NS (Next Key Share) | 行 | RS/CS 級別下替代 S 鎖,可讀不可改 |
| S (Share) | 表、行 | 可讀不可改,他人可讀不可改 |
| IX (Intent Exclusive) | 表空間、表 | 可讀寫,讀取行加 S,更新行加 U/X;他人可讀寫 |
| SIX (Share With Intent Exclusive) | 表 | 可讀寫,更新行加 X,讀取行不加鎖;他人可讀不可改 |
| U (Update) | 表、行 | 可更新,自動升級為 X;他人可讀不可改 |
| NW (Next Key Weak Exclusive) | 行 | 非編目索引插入時,對下一行加此鎖 |
| X (Exclusive) | 表、行 | 可讀寫,僅 UR 級別可存取;INSERT/UPDATE/DELETE 會取得 |
| WE (Weak Exclusive) | 行 | 非編目表插入行時加此鎖 |
| Z (Super Exclusive) | 表空間、表 | DDL 操作(ALTER/DROP/CREATE INDEX)自動取得,獨佔全表 |
DB2 傾向隱式獲取行級鎖。可透過:
ALTER TABLE t LOCKSIZE TABLE — 強制該表所有事務用表級鎖LOCK TABLE t IN SHARE|EXCLUSIVE MODE — 單一事務主動請求表級鎖鎖升級:當鎖記憶體不足(受 maxlocks 限制),自動將多個行鎖合併為單一表鎖,釋放空間。
ALTER TABLE ... LOCKSIZE ROW/TABLE 與 LOCK TABLE 靈活控制依 SQL 性質分四類,使用不同鎖組合:
| 類型 | 典型 SQL | 常用鎖模式 |
|---|---|---|
| 唯讀 | SELECT、FOR READ ONLY | IS、S |
| 傾向更改 | SELECT FOR UPDATE | U、IX、X(表);S、U、X(行) |
| 更改 | INSERT/UPDATE/DELETE | IX、X |
| 游標控制 | WHERE CURRENT OF | IX、X |
優化器選擇索引掃描傾向行鎖(IS);表掃描可能直接取表鎖(S)。良好的索引設計能減少鎖衝突。
掌握這些核心機制,不僅能通過 730 認證,更能在實戰中設計出高並發、強一致的 DB2 應用系統。