MySQL 交易(Transaction)
START TRANSACTION / COMMIT / ROLLBACK
簡介
在資料庫開發中,資料完整性與一致性是最根本的要求。即使是最簡單的增刪改操作,也可能因為網路斷線、程式錯誤或同時寫入衝突而留下不完整或不正確的資料。
MySQL 透過 交易(Transaction) 機制,讓一組相關的 SQL 敘述能夠 原子性(atomic)地執行:要麼全部成功,要麼全部失敗,資料庫永遠不會處於半完成的狀態。
本篇文章聚焦於 MySQL 中最常使用的三個指令:
START TRANSACTION(或BEGIN)COMMITROLLBACK
我們將從概念說明、實作範例、常見陷阱與最佳實踐,最後延伸到真實的應用情境,幫助你在日常開發中正確、有效地使用交易。
核心概念
1. 什麼是交易?
交易是一組 邏輯上相關 的 SQL 語句,具備以下四大特性(ACID):
| 特性 | 說明 |
|---|---|
| Atomic(原子性) | 所有語句要麼全部成功,要麼全部失敗。 |
| Consistency(一致性) | 交易執行前後,資料庫必須保持一致的狀態。 |
| Isolation(隔離性) | 交易之間互不干擾,避免讀到未提交的資料。 |
| Durability(永久性) | 一旦提交(COMMIT),變更即永久寫入磁碟,即使系統崩潰也不會遺失。 |
小提醒:MySQL 的預設儲存引擎 InnoDB 完全支援 ACID;若使用 MyISAM,則不支援交易。
2. 開始交易:START TRANSACTION / BEGIN
START TRANSACTION;
-- 或者簡寫
BEGIN;
兩者等價,會把連線的 自動提交模式(autocommit) 暫時關閉,直到執行 COMMIT 或 ROLLBACK 為止。此時,所有後續的 DML(INSERT、UPDATE、DELETE)都會被暫存於交易緩衝區。
3. 提交交易:COMMIT
COMMIT;
COMMIT 會把交易緩衝區的變更永久寫入磁碟,並自動恢復自動提交模式。此時其他連線才能看到這些變更。
4. 回滾交易:ROLLBACK
ROLLBACK;
若在交易過程中發生錯誤,或是業務邏輯判斷需要取消操作,執行 ROLLBACK 就會將所有變更撤銷,回到交易開始前的狀態。
程式碼範例
以下示範 5 個常見情境,讓你快速掌握交易的使用方式。
範例 1:簡單的扣款與入帳(原子性)
START TRANSACTION;
UPDATE accounts SET balance = balance - 100
WHERE user_id = 1 AND balance >= 100;
UPDATE accounts SET balance = balance + 100
WHERE user_id = 2;
COMMIT;
說明:若第一筆
UPDATE因餘額不足而影響 0 行,整筆交易仍會提交。實務上應先檢查ROW_COUNT(),若不足則ROLLBACK。
範例 2:失敗時回滾(錯誤處理)
START TRANSACTION;
INSERT INTO orders (user_id, total) VALUES (1, 250);
SET @order_id = LAST_INSERT_ID();
INSERT INTO order_items (order_id, product_id, qty) VALUES
(@order_id, 101, 2),
(@order_id, 102, 1);
-- 假設第二筆 INSERT 因外鍵違反失敗
-- MySQL 會拋出錯誤,我們在應用程式端捕獲後執行 ROLLBACK
ROLLBACK;
說明:在程式碼(如 PHP、Node.js)捕獲例外後,務必呼叫
ROLLBACK,避免資料遺留下半完成的紀錄。
範例 3:使用 SAVEPOINT 與部分回滾
START TRANSACTION;
INSERT INTO inventory (product_id, qty) VALUES (200, 50);
SAVEPOINT after_inventory;
INSERT INTO shipments (product_id, qty, dest) VALUES (200, 30, 'TW');
-- 假設運送資料有誤,只想回到 inventory 的狀態
ROLLBACK TO SAVEPOINT after_inventory;
COMMIT;
說明:
SAVEPOINT允許在同一筆交易內設定多個回滾點,靈活控制哪一段需要撤銷。
範例 4:設定交易隔離等級(Isolation Level)
-- 讀取未提交的資料(Dirty Read)-- 只在測試環境使用
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 3;
COMMIT;
說明:MySQL 支援四種隔離等級(READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE),預設為
REPEATABLE READ,大多數情況不需要更改。
範例 5:自動提交模式的切換
-- 關閉自動提交,等同於每次手動 START TRANSACTION
SET autocommit = 0;
UPDATE products SET stock = stock - 5 WHERE product_id = 400;
UPDATE orders SET status = 'processed' WHERE order_id = 789;
COMMIT; -- 必須手動提交
SET autocommit = 1; -- 恢復預設
說明:直接關閉
autocommit會讓所有後續語句自動納入同一筆交易,適合批次腳本或長時間執行的維護作業。
常見陷阱與最佳實踐
| 陷阱 | 可能的影響 | 解決方式 |
|---|---|---|
忘記 COMMIT |
交易永遠不會釋放鎖,導致其他連線阻塞。 | 確保每條 START TRANSACTION 最終都有 COMMIT 或 ROLLBACK,可在程式碼中使用 finally 區塊保證。 |
在 READ COMMITTED 以外的隔離等級下使用長交易 |
產生大量的 undo log,佔用磁碟空間,甚至觸發「死鎖」。 | 盡量讓交易保持短小,只包住必要的語句。 |
| 混用自動提交與手動交易 | 可能導致部分語句提前提交,破壞原子性。 | 統一使用手動交易,或在需要時暫時關閉 autocommit。 |
| 忽略錯誤回傳 | 交易失敗卻仍執行 COMMIT,造成不一致資料。 |
在程式層捕捉例外,發生錯誤立即 ROLLBACK。 |
| 使用 MyISAM 引擎 | 完全不支援交易,所有 START TRANSACTION 會被忽略。 |
改用 InnoDB,或確認表格已轉換為支援交易的引擎。 |
最佳實踐
- 最小化交易範圍:只把必須一起成功的語句放入同一筆交易,減少 lock 持有時間。
- 明確檢查受影響列數:
ROW_COUNT()、FOUND_ROWS()能幫助判斷是否真的執行成功。 - 使用
SAVEPOINT:在複雜流程中加入回滾點,避免整筆交易回滾。 - 設定合理的隔離等級:大多數應用使用預設的
REPEATABLE READ;若讀取大量報表,可考慮READ COMMITTED以降低快照成本。 - 監控與日誌:將每次
START TRANSACTION、COMMIT、ROLLBACK記錄到應用層日誌,便於排錯與審計。
實際應用場景
| 場景 | 為何需要交易 | 典型 SQL 流程 |
|---|---|---|
| 電商訂單處理 | 同時扣減庫存、產生訂單、更新客戶積分,必須全部成功。 | START TRANSACTION → INSERT orders → UPDATE inventory → UPDATE members → COMMIT |
| 銀行轉帳 | 兩筆帳戶餘額的變更必須同步完成,避免出現「負餘額」或「金額遺失」。 | START TRANSACTION → UPDATE from_account → UPDATE to_account → IF ROW_COUNT()=2 THEN COMMIT ELSE ROLLBACK |
| 批次匯入資料 | 大量寫入時若發現資料錯誤,需要一次回滾,保證匯入結果全或無。 | SET autocommit=0; START TRANSACTION; LOAD DATA ...; IF error THEN ROLLBACK ELSE COMMIT; SET autocommit=1; |
| 多階段審批流程 | 每個審批階段的變更需保留在同一筆交易中,確保流程不被中斷。 | START TRANSACTION → UPDATE approvals → INSERT logs → COMMIT |
| 報表快照 | 為了避免讀到未提交的變更,使用 READ COMMITTED 或 REPEATABLE READ 產生一致性快照。 |
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT ...; COMMIT; |
總結
- 交易是保證資料完整性的核心機制,
START TRANSACTION、COMMIT、ROLLBACK三指令構成了最基本的使用方式。 - 充分理解 ACID 四大特性,才能在設計資料庫操作時正確運用交易。
- 常見的陷阱包括忘記提交、長時間持有鎖、混用自動提交等,最佳實踐是保持交易短小、明確檢查執行結果、適時使用
SAVEPOINT。 - 在電商、金融、批次匯入等實務場景中,交易是不可或缺的保護層,能讓系統在面對錯誤或異常時仍然維持 一致性 與 可靠性。
掌握了這些概念與技巧,你就能在 MySQL 上建立穩健、可擴充的資料庫應用,讓業務邏輯與資料安全同時得到保障。祝開發順利!