下面我會從“事務是什么”→“為什么需要事務”→“事務的四大特性(ACID)”→“MySQL中怎么用事務”→“常見坑與調試技巧”→“完整實戰案例(含代碼、輸出、講解)”六個層次,給你一個“看完就能上手”的MySQL事務速查手冊。所有SQL均在MySQL 8.0驗證通過,默認存儲引擎InnoDB。
一、事務是什么?
事務(Transaction)是一組要么全部成功、要么全部失敗的SQL語句集合。
典型場景:銀行轉賬——A扣錢、B加錢兩步必須同時成功,否則回滾。
二、為什么需要事務?
- 并發場景下避免臟讀、不可重復讀、幻讀
- 硬件故障或程序崩潰時保證數據一致性
- 業務規則要求“全-or-無”語義
三、ACID 四字口訣
特性 | 解釋 | MySQL實現機制 |
---|---|---|
原子性 Atomicity | 全部成功或全部回滾 | undo log(回滾日志) |
一致性 Consistency | 事務前后數據庫狀態合法(約束、觸發器等) | 約束+undo/redo |
隔離性 Isolation | 并發事務互不干擾 | 鎖+MVCC |
持久性 Durability | 提交后永久生效 | redo log(重做日志)+雙寫緩沖 |
四、MySQL事務語法速查
- 基本流程
START TRANSACTION; -- 或 BEGIN;
-- DML語句1…n
COMMIT; -- 提交
ROLLBACK; -- 回滾
- 自動提交開關
SELECT @@autocommit; -- 1表示自動提交,0表示手動
SET autocommit=0; -- 當前會話關閉自動提交
- 保存點(部分回滾)
START TRANSACTION;
SAVEPOINT sp1;
DELETE FROM user WHERE id=1;
SAVEPOINT sp2;
UPDATE user SET money=100 WHERE id=2;
ROLLBACK TO sp2; -- 只回滾到sp2,保留sp1之前的操作
- 隱式提交(陷阱)
DDL(CREATE/ALTER/DROP)、鎖表、ANALYZE、LOAD DATA等語句會強制提交當前事務。
五、隔離級別與并發問題
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 加鎖讀 | 語句 |
---|---|---|---|---|---|
READ UNCOMMITTED | √ | √ | √ | 否 | |
READ COMMITTED | × | √ | √ | 否 | Oracle默認 |
REPEATABLE READ | × | × | ×* | 否 | MySQL默認 |
SERIALIZABLE | × | × | × | 是 | 鎖表 |
*InnoDB通過間隙鎖+MVCC在REPEATABLE READ下也解決了幻讀,因此大多數業務無需跳到SERIALIZABLE。
六、完整實戰:銀行轉賬(含異常回滾演示)
- 表結構
CREATE DATABASE IF NOT EXISTS demo_tx;
USE demo_tx;
CREATE TABLE account(id INT PRIMARY KEY,name VARCHAR(20) UNIQUE,money DECIMAL(10,2) NOT NULL CHECK (money>=0)
) ENGINE=InnoDB;INSERT INTO account VALUES
(1,'Alice',1000),
(2,'Bob',1000);
- 存儲過程:安全轉賬
DELIMITER $$
CREATE PROCEDURE sp_transfer(IN from_id INT,IN to_id INT,IN amount DECIMAL(10,2)
)
BEGINDECLARE EXIT HANDLER FOR SQLEXCEPTIONBEGINROLLBACK;SELECT 'Transfer failed, rolled back!' AS msg;END;START TRANSACTION;-- 1. 檢查余額IF (SELECT money FROM account WHERE id=from_id) < amount THENSIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Insufficient balance';END IF;-- 2. 扣錢UPDATE account SET money = money - amount WHERE id=from_id;-- 3. 加錢UPDATE account SET money = money + amount WHERE id=to_id;COMMIT;SELECT 'Transfer succeeded!' AS msg;
END$$
DELIMITER ;
-
測試場景
| 步驟 | 會話A(正常轉賬) | 會話B(并發讀) | 結果 |
|—|—|—|—|
| T1 |CALL sp_transfer(1,2,200);
| | Alice:800, Bob:1200 |
| T2 | |SELECT * FROM account;
| 讀到提交后最新值(READ COMMITTED) |
| T3 | 故意制造異常:扣完錢后插入違反唯一約束 | | 觸發EXIT HANDLER,自動ROLLBACK,雙方余額不變 | -
觀察undo/redo(可驗證)
-- 查看當前活躍事務
SELECT * FROM information_schema.innodb_trx\G-- 查看鎖等待
SELECT * FROM sys.innodb_lock_waits\G
七、常見坑與調試技巧
- 忘記COMMIT,導致長事務——
SELECT * FROM information_schema.processlist WHERE time>10;
- 自動提交=1,START TRANSACTION后仍被隱式提交——用
SELECT @@autocommit;
確認 - DDL打斷事務——把建索引、加字段操作放在業務低峰期
- 死鎖——InnoDB自動回滾代價最小的事務;應用層捕獲
1213 Deadlock
錯誤重試即可 - 批量插入性能——用
START TRANSACTION; ...bulk inserts... COMMIT;
比逐條autocommit快1~2個數量級
八、一句話總結
“BEGIN → 改數據 → 沒問題COMMIT,出問題ROLLBACK”是事務90%的工作量;剩下10%在于選對隔離級別、避免長事務、監控鎖等待。把本文的存儲過程模板復制到測試庫跑一遍,你就擁有了可落地的MySQL事務最佳實踐。