MySQL 事務支援自 3.23 版本引入 (需 BDB/InnoDB),現代應用以 InnoDB 為主。本文系統解析 ACID 四大特性、InnoDB 事務實作、行鎖機制、隔離級別、死鎖處理與生產級最佳實踐。
| 特性 | 英文 | 核心含義 | 銀行轉帳例子 |
|---|---|---|---|
| 原子性 | Atomicity | 事務是不可分割的最小單元,要麼全做要麼全不做 | 扣款與入帳同時成功或同時失敗,不會只扣不入 |
| 一致性 | Consistency | 事務前後資料庫從一個一致狀態轉移到另一個一致狀態 | 總金額守恆,無憑空產生/消失的錢 |
| 隔離性 | Isolation | 並發事務互不干擾,中間狀態不可見 | A 轉帳未提交時,B 查詢余額不受影響 |
| 持久性 | Durability | 提交後的修改永久保存,不怕掉電、崩潰 | 轉帳完成後,重啟資料庫帳目仍正確 |
innodb_log_file_size)
-- 開始事務 (三種等效)
START TRANSACTION;
BEGIN;
BEGIN WORK;
-- 提交
COMMIT;
-- 回滾
ROLLBACK;
-- 保存點 (部分回滾)
SAVEPOINT sp1;
ROLLBACK TO SAVEPOINT sp1;
RELEASE SAVEPOINT sp1;
-- 自動提交控制
SET AUTOCOMMIT = 0; -- 關閉,需手動 COMMIT
SET AUTOCOMMIT = 1; -- 開啟 (預設)
-- Session A -- Session B
BEGIN; BEGIN;
SELECT qty FROM book WHERE id=1; -- 讀到 qty=1
SELECT qty FROM book WHERE id=1; -- 讀到 qty=1 (髒讀/不可重複讀)
UPDATE book SET qty=0 WHERE id=1;
COMMIT;
UPDATE book SET qty=0 WHERE id=1; -- 超賣!qty 變 -1
COMMIT;
-- Session A -- Session B
BEGIN; BEGIN;
SELECT qty FROM book WHERE id=1 FOR UPDATE; -- 加排他鎖 (X鎖)
-- 這裡阻塞等待...
UPDATE book SET qty=0 WHERE id=1;
COMMIT; -- Session A 提交,釋放鎖
-- Session B 獲得鎖,讀到 qty=0
SELECT qty FROM book WHERE id=1 FOR UPDATE; -- 發現庫存 0
-- 業務邏輯判斷庫存不足
ROLLBACK;
| 語句 | 鎖類型 | 相容性 | 用途 |
|---|---|---|---|
SELECT ... LOCK IN SHARE MODE | 共享鎖 (S鎖) | S鎖相容、X鎖互斥 | 讀取並防止他人修改 |
SELECT ... FOR UPDATE | 排他鎖 (X鎖) | S/X 鎖皆互斥 | 讀取並準備修改 (核心業務) |
UPDATE/DELETE/INSERT | 隱式 X鎖 | X鎖互斥 | 寫操作自動加鎖 |
| 級別 | 髒讀 | 不可重複讀 | 幻讀 | InnoDB 實作 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 直接讀最新版本,不加鎖 |
| READ COMMITTED | ❌ | ✅ | ✅ | 每次 SELECT 讀最新提交版本 (需 READ_COMMITTED 模式) |
| REPEATABLE READ | ❌ | ❌ | ❌* | 預設。事務啟動時建立 Read View,全程讀同一快照。間隙鎖解決幻讀 |
| SERIALIZABLE | ❌ | ❌ | ❌ | 讀加 S鎖、寫加 X鎖,實質單執行緒 |
* InnoDB REPEATABLE READ 配合 Next-Key Lock (行鎖+間隙鎖) 在大多數場景解決幻讀,但極端情況 (如唯一鍵衝突) 仍可能發生。
-- 會話級
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 全域級 (需重啟)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 或 my.cnf: transaction-isolation = READ-COMMITTED
-- Session A -- Session B
BEGIN; BEGIN;
UPDATE account SET bal=bal-100 WHERE id=1; -- 鎖 id=1
UPDATE account SET bal=bal-100 WHERE id=2; -- 鎖 id=2
UPDATE account SET bal=bal+100 WHERE id=2; -- 等待 id=2
UPDATE account SET bal=bal+100 WHERE id=1; -- 等待 id=1
-- DEADLOCK! -- DEADLOCK!
ERROR 1213 (HY000): Deadlock found when trying to get lock; try restarting transactionFOR UPDATEtry { ... commit } catch { rollback; throw }autoCommit=falseinnodb_print_all_deadlocks=ON、information_schema.INNODB_TRXMySQL/InnoDB 事務以 Redo Log 保證持久性、Undo Log+MVCC 實現隔離性、行鎖/間隙鎖協調並發。SELECT ... FOR UPDATE 是解決並發寫入衝突的核心武器,固定順序加鎖、縮短事務、適當隔離級別 是避免死鎖的鐵律。理解底層機制,才能在高併發業務中做出正確的架構決策。