Home
Posts
Categories
Series
Tags
About
交易併發問題
postedOn: 2026-4-12   updatedOn: 2026-4-12   includedIn: 程式
wordsCount: 500   readingTime: 1 min   viewers:

What we talk this ?

介紹基本定義,沒搞清楚就不知道交易隔離等級解決了什麼問題

交易併發常見問題

定義:

問題類型 定義 直覺理解 PostgreSQL RR MySQL RR 備註
Dirty Read 讀到未提交資料 別人還沒 commit 你就看到 不會 不會 兩者皆透過 MVCC 避開。
Non-Repeatable Read 同行讀取結果不同 原本桌上的蘋果被換了 不會 不會 皆透過 Snapshot (快照) 鎖定該行版本。
Phantom Read 範圍查詢結果行數增減 桌上突然多了一顆蘋果 不會 ⚠️可能發生 PG 靠快照完全封鎖;MySQL 僅在加鎖讀時靠 Gap Lock 防禦,純 Select 有破綻。
Write Skew 跨行邏輯衝突 各自檢查合法,合併結果違法 會發生 會發生 RR 級別的通病,除非提升至Serializable或手動加鎖。

Write Skew 寫偏差

「每個 transaction 的判斷都正確,但一起執行就違反規則」

當你有「條件約束」時:

例如:

  • 至少一人在線
  • 餘額不能為負(跨帳戶)
  • 名額限制(例如最多 N 人)

👉 且條件是「跨多筆資料」

例子:

至少要有一位醫生值班

doctor on_call
A true
B true

兩個交易同時發生

1
2
3
4
5
6
7
8
9
SELECT COUNT(*) WHERE on_call = true  2
👉 OK,可以下班

UPDATE doctor SET on_call = false WHERE name = 'A'
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
SELECT COUNT(*) WHERE on_call = true  2
👉 OK,可以下班

UPDATE doctor SET on_call = false WHERE name = 'B'
doctor on_call
A false
B false

✔️ 方法 1:用 Serializable

1
2
3
tx, _ := db.BeginTx(ctx, &sql.TxOptions{
    Isolation: sql.LevelSerializable,
})

✔️ 方法 2:手動加鎖 相關列鎖住

1
SELECT ... FOR UPDATE