MySQL 事務(Transaction)詳解
1. 什么是事務?
事務(Transaction)是一組要么全部執行,要么全部回滾的 SQL 語句,用于保證數據一致性。事務一般用于銀行轉賬、訂單支付等操作,確保多個步驟要么全部成功,要么全部失敗。
事務的特點(ACID):
- 原子性(Atomicity):事務是一個不可分割的最小操作單位,要么全部執行,要么全部回滾。
- 一致性(Consistency):事務執行后,數據庫必須保持一致狀態。
- 隔離性(Isolation):多個事務并發執行時,相互之間不會影響。
- 持久性(Durability):事務提交后,對數據庫的更改是永久性的。
2. 事務的基本操作
在 MySQL 中,事務通常用 START TRANSACTION
或 BEGIN
開始,COMMIT
提交,ROLLBACK
回滾:
-- 開啟事務
START TRANSACTION;-- 執行 SQL 語句
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;-- 提交事務(數據永久生效)
COMMIT;
如果中途發生錯誤,可以回滾:
-- 開啟事務
START TRANSACTION;-- 執行 SQL 語句
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 發生錯誤,回滾事務
ROLLBACK;
3. 事務的隔離級別
MySQL 事務的隔離級別影響并發事務的執行方式,常見隔離級別:
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 默認值 |
---|---|---|---|---|
READ UNCOMMITTED(讀未提交) | ? 可能 | ? 可能 | ? 可能 | ? |
READ COMMITTED(讀已提交) | ? 避免 | ? 可能 | ? 可能 | ? |
REPEATABLE READ(可重復讀) | ? 避免 | ? 避免 | ? 可能 | ?(MySQL 默認) |
SERIALIZABLE(串行化) | ? 避免 | ? 避免 | ? 避免 | ? |
如何設置隔離級別?
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
4. 事務的常見問題
(1)臟讀(Dirty Read)
發生在 READ UNCOMMITTED
級別:一個事務能讀取另一個未提交事務的數據,如果對方回滾,讀取的數據就是無效的。
-- 事務 A
START TRANSACTION;
UPDATE accounts SET balance = 500 WHERE user_id = 1;
-- 此時事務 A 還未提交-- 事務 B
SELECT balance FROM accounts WHERE user_id = 1; -- 讀取到了未提交的值
? 解決方案:使用 READ COMMITTED
以上隔離級別。
(2)不可重復讀(Non-Repeatable Read)
發生在 READ COMMITTED
級別:同一個事務多次讀取數據,發現數據不一致(另一事務修改了數據并提交)。
-- 事務 A
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 1; -- 讀取值 500-- 事務 B(修改后提交)
UPDATE accounts SET balance = 600 WHERE user_id = 1;
COMMIT;-- 事務 A(再次讀取)
SELECT balance FROM accounts WHERE user_id = 1; -- 發現變成 600
? 解決方案:使用 REPEATABLE READ
以上隔離級別。
(3)幻讀(Phantom Read)
發生在 REPEATABLE READ
級別:事務中多次查詢,發現新增的行。
-- 事務 A
START TRANSACTION;
SELECT COUNT(*) FROM orders WHERE user_id = 1; -- 10 條數據-- 事務 B(插入新數據并提交)
INSERT INTO orders(user_id, amount) VALUES (1, 100);
COMMIT;-- 事務 A(再次查詢)
SELECT COUNT(*) FROM orders WHERE user_id = 1; -- 發現 11 條數據
? 解決方案:使用 SERIALIZABLE
級別,防止新數據插入。
5. InnoDB 與 MyISAM 事務支持
存儲引擎 | 事務支持 | 適用場景 |
---|---|---|
InnoDB | ? 支持事務 | 適合高并發、數據一致性要求高的系統,如金融、訂單 |
MyISAM | ? 不支持事務 | 適合讀取操作多的場景,如 CMS 系統 |
如何查看表的存儲引擎?
SHOW TABLE STATUS WHERE Name = 'my_table';
6. MySQL 事務最佳實踐
- 盡量使用 InnoDB,保證事務支持。
- 縮小事務范圍,避免長時間鎖表影響性能。
- 避免事務嵌套,減少鎖競爭。
- 使用索引優化查詢,減少鎖的范圍,提高并發效率。
- 發生錯誤時記得
ROLLBACK
,確保數據一致性。 - 適當選擇隔離級別,根據業務需求權衡并發性和數據一致性。
7. 事務相關的 SQL 語句
操作 | SQL 語句 |
---|---|
開啟事務 | START TRANSACTION; 或 BEGIN; |
提交事務 | COMMIT; |
回滾事務 | ROLLBACK; |
設置隔離級別 | SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; |
查看當前事務隔離級別 | SELECT @@TRANSACTION_ISOLATION; |
8. 總結
- 事務保證 ACID,保證數據庫的一致性。
- MySQL 事務隔離級別決定數據并發訪問時的行為,
REPEATABLE READ
是 MySQL 默認隔離級別。 - 事務的三大問題:臟讀、不可重復讀、幻讀,可通過不同的隔離級別解決。
- InnoDB 支持事務,MyISAM 不支持。
- 務必在事務結束后
COMMIT
或ROLLBACK
,防止事務長時間占用資源。