事務通常是「全提交或全回滾」,但業務有時需要「提交前置操作,回滾後續嘗試」。MySQL SAVEPOINT 機制完美解決此需求。
| 語句 | 作用 |
|---|---|
SAVEPOINT savepoint_name | 在當前事務中標記一個保存點 |
ROLLBACK TO SAVEPOINT savepoint_name | 回滾到指定保存點,釋放該點之後的鎖,保留之前的操作 |
RELEASE SAVEPOINT savepoint_name | 顯式釋放保存點(不影響數據),節省內存 |
-- 1. 準備表 CREATE TABLE `user` (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20)); INSERT INTO `user` VALUES (1, 'Alice'); -- 2. 開啟事務 START TRANSACTION; -- 3. 插入第 1 條 INSERT INTO `user` VALUES (2, 'Bob'); -- 4. 插入第 2 條 INSERT INTO `user` VALUES (3, 'Carol'); -- 5. 設置保存點 SAVEPOINT after_two; -- 6. 插入第 3 條(可能失敗的業務嘗試) INSERT INTO `user` VALUES (4, 'Dave'); -- 7. 假設業務判斷失敗,回滾到保存點 ROLLBACK TO SAVEPOINT after_two; -- 8. 查看結果:只有 Alice, Bob, Carol(id=1,2,3) SELECT * FROM `user`; -- 9. 繼續其他操作或提交 COMMIT;
ROLLBACK TO SAVEPOINT 釋放保存點後獲取的鎖,保留之前的鎖ROLLBACK TOCOMMIT 清除所有保存點,ROLLBACK(無 TO)回滾整個事務並清除保存點START TRANSACTION;
-- 1. 創建訂單主表(核心,必須成功)
INSERT INTO orders (user_id, total) VALUES (1001, 299.00);
SET @order_id = LAST_INSERT_ID();
-- 2. 寫入訂單項(核心)
INSERT INTO order_items (order_id, sku_id, qty, price) VALUES
(@order_id, 1001, 1, 199.00),
(@order_id, 1002, 2, 50.00);
SAVEPOINT after_core; -- 核心業務完成
-- 3. 嘗試扣減庫存(可能失敗:庫存不足、並發衝突)
UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock >= 1;
UPDATE products SET stock = stock - 2 WHERE id = 1002 AND stock >= 2;
-- 4. 檢查庫存扣減結果
IF ROW_COUNT() < 2 THEN
ROLLBACK TO SAVEPOINT after_core; -- 僅回滾庫存操作,保留訂單
-- 記錄庫存不足日誌、發送補貨通知
INSERT INTO stock_alert (sku_id, reason) VALUES (1001, 'INSUFFICIENT');
ELSE
-- 庫存扣減成功,繼續發送通知、積分等非核心操作
INSERT INTO notifications (order_id, type) VALUES (@order_id, 'CREATED');
END IF;
COMMIT;
MySQL 不支持嵌套事務(START TRANSACTION 會隱式提交前一個事務)。保存點是單事務內實現「部分提交/回滾」的唯一標準方式。
ROLLBACK TO SAVEPOINT 後事務還活著嗎?COMMIT/ROLLBACK。SAVEPOINT abc 和 SAVEPOINT ABC 視為同一個。ROLLBACK TO SAVEPOINT。after_core、before_risky、before_external_callRELEASE SAVEPOINT 釋放不再需要的點,避免元數據膨脹善用 SAVEPOINT,讓事務控制力度從「粗粒度全有或全無」進化為「細粒度可控回滾」。