什麼是 MySQL 事務?¶
想象一下,你去銀行給朋友轉賬:先從你的賬戶扣錢,再往朋友賬戶加錢。這兩個步驟必須同時成功,否則錢就會莫名其妙消失或多出來。MySQL 事務就像這個場景:它是一組 SQL 操作的集合,要麼全部執行成功(提交),要麼全部失敗(回滾),不會出現“一半成功一半失敗”的中間狀態。
事務的四大特性(ACID)¶
事務之所以可靠,是因爲它滿足 ACID 四個核心特性,每個特性都解決了不同的問題:
1. 原子性(Atomicity)¶
“原子”的意思是“不可分割”。事務中的所有操作要麼全部完成,要麼一個都不做。
例子:轉賬時,“扣錢”和“加錢”必須同時成功或失敗。如果扣錢成功但加錢失敗,系統會自動回滾,讓你回到最初的狀態(比如轉賬前的賬戶餘額)。
2. 一致性(Consistency)¶
事務執行前後,數據必須符合業務規則。比如“總金額不變”:你轉賬 100 元,A 賬戶減 100,B 賬戶加 100,最終 A+B 的總金額和轉賬前一致。
關鍵點:一致性是事務的最終目標,由原子性和其他特性共同保證。
3. 隔離性(Isolation)¶
多個事務同時執行時,彼此之間不會互相干擾。比如你和朋友同時給同一個賬戶轉賬,你的操作不會影響朋友的操作,朋友的操作也不會讓你看到“未完成的中間狀態”。
注意:隔離性解決的是“併發問題”,不同的隔離級別會影響數據的可見性。
4. 持久性(Durability)¶
事務提交後,數據會永久保存,即使系統崩潰也不會丟失。比如轉賬成功後,就算數據庫重啓,A 和 B 賬戶的餘額也不會變。
爲什麼需要事務?典型使用場景¶
事務在實際業務中無處不在,核心是保證數據的“準確性”和“完整性”:
1. 銀行轉賬¶
- 場景:從 A 賬戶向 B 賬戶轉 1000 元。
- 問題:如果只扣 A 賬戶不增 B 賬戶,A 會少錢,B 沒收到錢;反之亦然。
- 解決方案:用事務包裹
UPDATE A 賬戶和UPDATE B 賬戶,成功則提交,失敗則回滾。
2. 電商訂單處理¶
- 場景:用戶下單時,需要同時完成“創建訂單”和“扣減庫存”兩個操作。
- 問題:如果只創建訂單不扣庫存,會導致“超賣”(比如 10 件商品被 100 個用戶下單,庫存顯示 10 但實際賣了 100 件)。
- 解決方案:事務確保“下單+扣庫存”要麼同時成功,要麼同時失敗。
3. 支付系統¶
- 場景:用戶支付成功後,需要更新訂單狀態、扣減用戶餘額、記錄支付流水。
- 問題:如果其中一個步驟失敗(比如支付成功但訂單狀態沒更新),用戶可能重複支付。
- 解決方案:事務保證所有相關操作“要麼全做,要麼全不做”。
MySQL 中如何使用事務?¶
MySQL 中只有 InnoDB 引擎支持事務,MyISAM 等引擎不支持。以下是基本操作步驟:
1. 顯式開啓事務¶
默認情況下,MySQL 可能開啓了“自動提交”(每條 SQL 語句都是獨立事務),需顯式開啓事務:
START TRANSACTION; -- 或 BEGIN;
2. 執行 SQL 操作¶
在事務中執行需要保證一致性的 SQL 語句,比如轉賬示例:
-- 假設 A 賬戶 ID=1,B 賬戶 ID=2,轉賬金額=100
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
3. 提交或回滾事務¶
- 提交(COMMIT):事務中所有操作成功完成後,用
COMMIT確認修改永久生效。 - 回滾(ROLLBACK):如果操作出錯(比如餘額不足),用
ROLLBACK撤銷所有修改,恢復到事務開始前的狀態。
示例:
START TRANSACTION;
-- 執行操作...
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 檢查是否有錯誤(比如餘額是否足夠)
IF 操作成功 THEN
COMMIT; -- 提交,修改永久生效
ELSE
ROLLBACK; -- 回滾,撤銷所有修改
END IF;
事務隔離級別與常見問題¶
多個事務併發執行時,可能出現以下問題,需通過“隔離級別”解決:
1. 常見併發問題¶
- 髒讀:一個事務讀取到另一個事務未提交的數據(比如 A 事務還沒提交扣錢操作,B 事務就看到了 A 扣了錢)。
- 不可重複讀:同一事務內多次讀取同一數據,結果不同(比如 A 事務第一次讀餘額是 1000,中間 B 事務扣了錢,A 再次讀變成 900)。
- 幻讀:同一事務內多次查詢,結果集行數不同(比如 A 事務第一次查“訂單總數”是 10 條,中間 B 事務新增了一條訂單,A 再次查變成 11 條)。
2. MySQL 事務隔離級別¶
MySQL InnoDB 支持 4 種隔離級別,默認是 REPEATABLE READ(可重複讀),可通過 SELECT @@tx_isolation; 查看當前級別:
- 讀未提交(READ UNCOMMITTED):最低級別,可能出現髒讀、不可重複讀、幻讀。
- 讀已提交(READ COMMITTED):避免髒讀,但可能有不可重複讀、幻讀(MySQL 中 SET TRANSACTION ISOLATION LEVEL READ COMMITTED;)。
- 可重複讀(REPEATABLE READ):MySQL 默認級別,通過多版本控制(MVCC)避免不可重複讀和幻讀(但可能有部分幻讀,具體看版本)。
- 串行化(SERIALIZABLE):最高級別,事務串行執行,完全避免併發問題,但性能最低。
注意事項¶
- 避免長事務:長事務會佔用數據庫資源,導致鎖表,影響併發性能。儘量拆分操作或縮短事務時間。
- 慎用自動提交:默認情況下,MySQL 可能開啓自動提交(
autocommit=1),需顯式用START TRANSACTION控制事務範圍。 - 隔離級別選擇:根據業務對一致性和性能的需求選擇,比如簡單查詢用讀已提交,核心業務(如支付)用可重複讀。
總結¶
事務是 MySQL 保證數據一致性的核心機制,通過 ACID 特性確保操作“要麼全做,要麼全不做”。理解事務的基本概念、使用場景和操作方法,能幫助你寫出更可靠的 SQL 代碼。在實際開發中,結合業務需求選擇合適的隔離級別,避免併發問題,是保證系統穩定性的關鍵。