CodeGym /課程 /SQL SELF /使用交易時的限制與潛在問題:LOCK、DEADLOCK

使用交易時的限制與潛在問題:LOCK、DEADLOCK

SQL SELF
等級 54, 課堂 1
開放

PostgreSQL 裡的交易(transaction)會保證隔離性——這是 ACID 的重點之一。為了做到這點,系統會用到鎖(lock)。鎖就是一種機制,確保多個交易不會「搶」同一筆資料或同一張表。想像你在超市排隊結帳:一次只能一個人結帳。鎖的運作方式就有點像這樣,只是它是在管控資料表裡的資料存取。

鎖的主要類型

在 PostgreSQL 裡有幾種不同的鎖,有些你可能已經在 EXPLAIN ANALYZE 裡看過:

  1. ROW EXCLUSIVE(列專屬鎖) — 當你改動某一列的資料時會出現。這是最常見的鎖類型,比如你在做 INSERT、UPDATE 或 DELETE 的時候。
  2. SHARE(共用鎖) — 用在那些需要保證資料不會被改動的操作,比如 SELECT ... FOR SHARE。
  3. EXCLUSIVE(專屬鎖) — 會阻擋其他所有操作,除了讀取。

可以說,鎖就像是看門狗一樣,守護著我們的資料。

鎖的問題:會出什麼狀況?

現在我們知道什麼是鎖了,來看看它們可能帶來什麼麻煩。最大條的問題就是死結(deadlock)。死結就是兩個(或更多)交易互相等對方釋放鎖,結果大家都卡住,進入無限等待的迴圈。通常會像這樣:

  1. 交易 1 鎖住了列 A,想要存取列 B。
  2. 交易 2 鎖住了列 B,想要存取列 A。
  3. 因為兩列都被鎖住,沒有一個交易能完成。

死結範例

想像一下工程師遇到 deadlock 的痛苦場景:

-- 交易 1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

-- 同時
-- 交易 2
BEGIN;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;

-- 交易 1 嘗試鎖住 id = 2
UPDATE accounts SET balance = balance - 100 WHERE id = 2;

-- 交易 2 嘗試鎖住 id = 1
UPDATE accounts SET balance = balance + 200 WHERE id = 1;

登愣!每個交易都被對方鎖住了。PostgreSQL 遲早會發現這種情況,然後丟出死結錯誤。

怎麼避免死結?

  1. 資料存取順序要一致。 盡量每次都用一樣的順序存取資料。如果交易 1 先鎖列 A 再鎖列 B,那交易 2 也要照這個邏輯:先列 A,再列 B。
-- 正確的存取順序
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
COMMIT;
  1. 讓交易盡量短。 交易拖太久,死結的機率就高。記得快點做完:BEGIN,做完所有要改的東西,馬上 COMMIT。

  2. 用適合的交易隔離等級。 如果你的需求沒那麼嚴格,可以考慮用 READ COMMITTED 隔離等級。這樣可以避掉一些鎖的問題。

鎖的 timeout 跟診斷

PostgreSQL 有一些工具可以幫你診斷和預防鎖的問題。比如你可以設定鎖的最大等待時間:

SET lock_timeout = '5s';  -- 設定鎖的 timeout 為 5 秒

如果鎖被持有超過這個時間,交易就會被中斷,這樣就不會整個卡死。

在 PostgreSQL 追蹤鎖

有個很實用的指令可以監控鎖,就是 pg_locks。它會顯示系統裡所有的鎖:

SELECT * FROM pg_locks;

你可以看到哪些交易持有鎖、哪些在等鎖。debug 死結時超級有用。

LOCK 的細節與手動鎖定物件

如果你想手動管理鎖,可以用 LOCK 指令:

LOCK TABLE orders IN ACCESS EXCLUSIVE MODE;

注意:ACCESS EXCLUSIVE 是最強的鎖,會擋掉所有其他操作,連 SELECT 都不行。只有在特殊情況(比如改表結構)才用它。

如果你只想鎖某幾列,可以用 SELECT ... FOR UPDATE:

SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

這樣可以保證其他交易在你這個交易結束前不能改這些列。

關於鎖你要記得什麼?

鎖不是壞東西,是工具。它們幫你守住資料完整性,但要小心用。幾個重點建議:

  1. 沒必要就不要開交易。
  2. 交易要快點結束。
  3. 存取資料順序要一致。
  4. 用適合你需求的隔離等級。

現在你已經準備好跟鎖和死結打交道了!祝你 pg_locks 在手,ACID 智慧常伴!

2
任務
SQL SELF, 等級 54, 課堂 1
上鎖
建立與查看鎖定
建立與查看鎖定
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION