All writing2026.09.22 · CS 411 · Database Systems · 2 min

Transactions, Serializability, and Isolation: Reasoning About Concurrent Writes

From ACID and schedules to conflict graphs, anomalies, isolation levels, locks, and evidence from two database sessions.

Concurrency problems are difficult because each transaction can be correct in isolation while their interleaving is wrong.

ACID is a set of responsibilities

  • Atomicity: all effects commit or none do.
  • Consistency: a valid transaction moves the database between valid states.
  • Isolation: concurrent execution behaves according to a declared contract.
  • Durability: committed effects survive failures covered by the system.

Consistency is not automatically invented by the database. Constraints and transaction logic must define the valid states.

Schedules and conflicts

A schedule interleaves reads and writes from transactions. Two operations conflict when they touch the same item, belong to different transactions, and at least one is a write.

T1: read(A)  write(A)
T2:          read(A)  write(A)

Build a precedence graph with one node per transaction. Add Ti → Tj when a conflicting operation from Ti occurs before one from Tj. A cycle means the schedule is not conflict-serializable.

In plain terms: the graph asks whether transactions can be placed in one consistent “happened before” order. A cycle says each would need to be before the other.

Common anomalies

Anomaly What changes unexpectedly
Dirty read A transaction sees another transaction’s uncommitted data
Non-repeatable read Re-reading one row returns a different committed value
Phantom Re-running a predicate returns a different set of rows
Lost update One write overwrites another transaction’s work
Write skew Two transactions preserve local checks but jointly break an invariant

Product names for isolation levels are similar, but exact guarantees differ by database. Verify the target system rather than relying only on the SQL-standard table.

Locks and MVCC solve different parts

Shared and exclusive locks coordinate conflicting access. Two-phase locking acquires locks in a growing phase and releases them in a shrinking phase. Strict 2PL holds write locks until commit, preventing other transactions from reading uncommitted writes and avoiding cascading aborts.

MVCC keeps row versions so readers can often proceed without blocking writers. It reduces contention; it does not remove write conflicts or every anomaly.

Reproduce a lost-update defense

Open two PostgreSQL sessions and run:

-- Setup
CREATE TABLE inventory(product_id integer PRIMARY KEY, quantity integer NOT NULL);
INSERT INTO inventory VALUES (42, 1);

Use one atomic statement from both sessions:

UPDATE inventory
SET quantity = quantity - 1
WHERE product_id = 42 AND quantity > 0
RETURNING quantity;

Only one transaction can return the decremented row. The other observes that the predicate no longer matches after lock acquisition.

Choose isolation from the invariant

Do not begin with “use serializable everywhere” or “read committed is faster.” Write the invariant, construct the dangerous interleaving, then choose an atomic statement, explicit lock, constraint, or stronger isolation that prevents it.

Review card

  • Correct transactions can form an incorrect schedule.
  • Conflicts require same item, different transactions, and a write.
  • A cycle in the precedence graph rejects conflict serializability.
  • MVCC improves reader/writer concurrency but does not erase anomalies.
  • Isolation names are not perfectly portable across products.
  • Start from the invariant and its dangerous interleaving.

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 開始設計。