Concurrency 難在每個 transaction 單獨看都正確,交錯執行後卻可能得到錯誤結果。
ACID 是四種責任
- Atomicity: 要嘛全部 commit,要嘛完全沒有 effect。
- Consistency: 合法 transaction 把資料庫從一個合法狀態帶到另一個。
- Isolation: Concurrent execution 遵守宣告的隔離 contract。
- Durability: Commit 的結果能承受系統承諾涵蓋的 failure。
資料庫不會自動發明 consistency rule;合法狀態仍要由 constraint 與 transaction logic 定義。
Schedule 與 conflict
Schedule 是多個 transaction read/write 的交錯順序。兩個 operation 同時符合以下條件才 conflict:操作同一個 item、來自不同 transaction,而且至少一個是 write。
T1: read(A) write(A)
T2: read(A) write(A)
Precedence graph 以 transaction 為 node。若 Ti 的 conflict operation 先於 Tj,加入 Ti → Tj。Graph 有 cycle,就不是 conflict-serializable schedule。
白話來說: Graph 在問這些 transaction 能不能排成一個一致的「誰先發生」順序。Cycle 代表每個 transaction 都必須排在另一個前面,無法成立。
常見 anomaly
| Anomaly | 意外改變的內容 |
|---|---|
| Dirty read | 讀到另一個 transaction 尚未 commit 的資料 |
| Non-repeatable read | 同一 row 再讀一次,得到不同 committed value |
| Phantom | 同一 predicate 再查一次,row set 改變 |
| Lost update | 一次 write 覆蓋另一個 transaction 的成果 |
| Write skew | 各自檢查都通過,合在一起卻破壞 invariant |
不同 database 雖然使用相似 isolation level 名稱,實際 guarantee 不完全相同。不能只背 SQL standard 表格,還要確認目標產品。
Lock 與 MVCC 解決不同部分
Shared/exclusive lock 協調 conflict access。Two-phase locking 在 growing phase 取得 lock,在 shrinking phase 釋放。Strict 2PL 把 write lock 保留到 commit,可避免其他 transaction 讀取 uncommitted write,也防止 cascading abort。
MVCC 保留 row version,讓 reader 經常不必 block writer。它降低 contention,不代表 write conflict 和所有 anomaly 都消失。
驗證 lost-update defense
開兩個 PostgreSQL session,先建立資料:
CREATE TABLE inventory(product_id integer PRIMARY KEY, quantity integer NOT NULL);
INSERT INTO inventory VALUES (42, 1);
兩邊都執行同一個 atomic statement:
UPDATE inventory
SET quantity = quantity - 1
WHERE product_id = 42 AND quantity > 0
RETURNING quantity;
只有一個 transaction 能成功回傳扣減後的 row。另一個取得 lock 後,會看到 predicate 已不再成立。
從 invariant 選 isolation
不要先決定「全部 serializable」或「read committed 比較快」。先寫下 invariant,構造會破壞它的 interleaving,再選 atomic statement、explicit lock、constraint 或更強 isolation。
複習卡
- 正確 transaction 仍可能組成錯誤 schedule。
- Conflict 必須是同 item、不同 transaction,而且包含 write。
- Precedence graph 有 cycle 就不是 conflict-serializable。
- MVCC 改善 reader/writer concurrency,不會消除所有 anomaly。
- Isolation level 名稱無法完全跨產品類比。
- 從 invariant 與危險 interleaving 開始設計。