在我們這門課裡,會三次聊到交易,每次都會發現新東西。現在先學交易的基礎。等到課程後半會講交易的隔離等級,最後還會聊巢狀交易的細節。
什麼是交易
先從簡單的定義開始。交易,就是一組只能一起執行的動作。 就這樣。
這裡有個重點:如果這組動作裡有任何一個失敗了,就要把其他所有動作的結果都撤銷,讓系統回到原本的狀態。
從 SQL 的角度來看,交易就是一組動作(像是插入、更新或刪除資料),它保證要嘛全部成功,要嘛一個都不做。這種行為讓資料庫操作變得很可靠又一致,特別是在像付款處理或關聯資料更新這種關鍵場景。
想像一下你要從一個銀行帳戶轉錢到另一個。如果簡化一下,就是兩個連續的操作:
- 從一個帳戶扣款。
- 把同樣的金額加到另一個帳戶。
如果在最後一步出錯(像是 server 掛掉),很重要的一點是不能讓任何一個用戶吃虧。換句話說,要嘛兩步都成功(交易被提交),要嘛兩步都撤銷(交易被取消)。
交易與 ACID 原則
交易是建立在 ACID 原則上的,這提醒我們有四個關鍵特性:
- Atomicity(原子性):全有或全無。交易裡的所有操作要嘛都做,要嘛都撤銷。
- Consistency(一致性):交易前後資料都要是正確的狀態。
- Isolation(隔離性):每個交易都像自己是系統裡唯一的。
- Durability(持久性):交易提交後,資料就算 server 掛掉也會被保存下來。
管理交易的基本指令
準備實作囉!這裡有三個管理交易最重要的指令:
BEGIN
開始一個新的交易。接下來的操作都會在這個交易裡面。COMMIT
提交變更。執行這個指令後,所有動作都會變成永久的。ROLLBACK
撤銷變更。如果有什麼出錯,可以用這個指令把交易撤回,資料會回到原本的狀態。
交易的基本語法
交易的簡單結構:
BEGIN;
-- 這裡寫你的 SQL 操作
COMMIT;
ROLLBACK 的範例:
BEGIN;
-- 修改 students 表格
UPDATE students
SET grade = grade + 10
WHERE id = 1;
-- 哦!我們發現這是個錯誤。
ROLLBACK;
實際任務裡用交易的例子
假設我們有這些表格:
students:
| id | name | grade |
|---|---|---|
| 1 | Otto Lin | 85 |
| 2 | Anna Song | 90 |
courses:
| course_id | course_name |
|---|---|
| 1 | 數學 |
| 2 | 歷史 |
假設我們想同時讓學生選課,還要更新他的平均分數:
BEGIN;
-- 步驟 1:在「選課記錄」表格加一筆
INSERT INTO course_enrollments (student_id, course_id)
VALUES (1, 2);
-- 步驟 2:更新學生的平均分數
UPDATE students
SET grade = grade + 5
WHERE id = 1;
COMMIT;
如果 server 在第一步和第二步之間掛掉會怎樣?如果沒用交易,資料就會不一致:選課記錄加進去了,但平均分數沒更新。不過有交易的話,兩個操作要嘛都做,要嘛都撤銷。
在交易裡處理錯誤
有時候事情會出錯,這時候要會正確處理錯誤。在 PostgreSQL 裡,只要出錯,交易會自動撤銷。
我們來故意出個錯看看。假設 course_enrollments 表格的 student_id 欄位有唯一性限制。現在我們試著加一筆重複的資料:
BEGIN;
INSERT INTO course_enrollments (student_id, course_id)
VALUES (1, 2);
-- 交易結束(還沒執行)
COMMIT;
如果插入一個已經選過課的學生,會出錯,PostgreSQL 會自動把交易撤銷。
用 ROLLBACK 手動撤銷
有時候錯誤很難預料,你可能會想在出錯時手動撤銷交易:
BEGIN;
-- 加一個新學生
INSERT INTO students (name, grade)
VALUES ('Omori Sanny', 75);
-- 哎呀!我們發現這個學生是誤加的。
ROLLBACK;
執行 ROLLBACK 之後,表格不會變—Omori Sanny 不會出現在 students 裡。
實用建議與常見錯誤
只要記住幾個重點,交易就會變得很好用:
- 只要操作有超過一個步驟,特別是要改多個表格時,一定要用交易。
- 千萬別忘了提交變更(
COMMIT)。不然交易會卡著沒完成,資料也不會變。 - 複雜的操作都包在交易裡,這樣資料才會一致。
- 只要任何步驟出錯,不要怕用
ROLLBACK。
現在你已經知道怎麼用交易來控制操作,是時候實作看看,繼續學習怎麼用交易來確保資料完整性囉!
GO TO FULL VERSION