巢狀交易:SAVEPOINT、ROLLBACK TO SAVEPOINT
在 PostgreSQL 裡,沒有真正的巢狀交易(nested transactions)這種東西啦,只有外層交易,然後裡面可以有一堆 savepoint 的「層」。
所謂的「巢狀交易」在 PostgreSQL 通常就是指用 savepoint 這個機制,搭配 SAVEPOINT、ROLLBACK TO SAVEPOINT、RELEASE SAVEPOINT 這幾個指令。這些不是獨立的交易,而是你在同一個外部交易裡設的 checkpoint,可以回到那個點,不用整個交易都回滾。
舉個生活例子:你在編輯器裡寫一大篇文章,偶爾會按 ctrl+s 存檔。如果你寫錯一堆,可以回到之前存的某個版本,不會整個進度都沒了。
管理 savepoint 的指令
要管理巢狀交易,PostgreSQL 給你三個主要指令:
SAVEPOINT
這個指令是用來建立「savepoint」,你之後可以回到這個點。它就像你交易裡的 checkpoint。
SAVEPOINT mypoint;
ROLLBACK TO SAVEPOINT
這個會回滾部分變更,就是從指定的 savepoint 之後的東西都撤銷,但更早的變更還會留著(都還在同一個外部交易裡)。
ROLLBACK TO SAVEPOINT mypoint;
RELEASE SAVEPOINT
這個是把 savepoint 刪掉,刪掉之後就不能再回到那個點了。
RELEASE SAVEPOINT mypoint;
範例:多表新增資料+可回滾
假設你在做一個訂單管理系統,要同時把資料存到兩個表:orders 跟 order_items。如果其中一個表新增失敗,不應該把另一個表的資料也撤銷。
BEGIN; -- 開始交易
-- 建立 savepoint
SAVEPOINT before_order;
-- 新增訂單到 orders 表
INSERT INTO orders (order_id, customer_id, date)
VALUES (1, 101, CURRENT_DATE);
-- 如果這裡出錯 — 回到這個 savepoint
SAVEPOINT before_order_items;
-- 新增商品到 order_items 表
INSERT INTO order_items (order_id, product_id, quantity)
VALUES (1, 2001, 4);
-- 如果有問題
-- ROLLBACK TO SAVEPOINT before_order_items;
-- 確認交易(commit 變更)
COMMIT;
如果你在新增 order_items 的時候出錯,可以回到 before_order_items,但 orders 表的變更還會保留。
實用建議跟常見錯誤
現在你已經知道 SAVEPOINT 跟 ROLLBACK TO SAVEPOINT 怎麼用了,這裡有幾個小建議,讓你少踩雷:
- savepoint 名稱要清楚。 取 savepoint 名字時,建議用有意義又獨特的名稱,像
before_insert、step1這種,debug 起來比較不會亂。 - 用完記得 release。 如果你確定不會再回到某個 savepoint,記得用
RELEASE SAVEPOINT把它刪掉,不然交易裡會一堆垃圾。 - 巢狀交易 ≠ 獨立交易。 要記得,當你
COMMIT之後,所有 savepoint 都沒了。外層COMMIT一下去,就不能再回滾囉。 - 資料鎖定。 就算你回到 savepoint,這個交易裡鎖住的資料還是會被鎖著。多人同時操作時要注意這點。
用 SAVEPOINT 跟 ROLLBACK TO SAVEPOINT 這種巢狀交易技巧,對開發者來說超方便,能處理很多複雜情境。你可以把交易拆成多個階段,錯誤處理也更細緻,不用每次都整個回滾。記住,只要看到「回滾」這個詞,不用太緊張,有時候回滾反而是最好的解法啦!
GO TO FULL VERSION