事務是數據庫區別於文件系統的核心特性。本文從原理到實戰,系統講解 ACID、隔離級別、鎖類型、死鎖處理、Savepoint 以及非事務引擎的替代方案。
| 特性 | 全稱 | MySQL/InnoDB 實現機制 |
|---|---|---|
| A | Atomicity(原子性) | Undo Log 記錄反操作,Crash 時回滾未提交事務 |
| C | Consistency(一致性) | 外鍵、唯一索引、檢查約束 + 原子性保證 |
| I | Isolation(隔離性) | MVCC + 行鎖/間隙鎖,四種隔離級別可調 |
| D | Durability(持久性) | Redo Log (WAL) + innodb_flush_log_at_trx_commit=1 刷盤 |
-- 顯式事務(推薦) START TRANSACTION; -- 或 BEGIN / BEGIN WORK UPDATE account SET balance = balance - 100 WHERE id = 1; UPDATE account SET balance = balance + 100 WHERE id = 2; COMMIT; -- 提交 -- ROLLBACK; -- 回滾 -- 隱式事務(autocommit=0) SET autocommit = 0; UPDATE ...; UPDATE ...; COMMIT; -- 必須顯式提交 -- 保存點(部分回滾) SAVEPOINT sp1; UPDATE ...; SAVEPOINT sp2; ROLLBACK TO SAVEPOINT sp1; -- 回滾到 sp1,sp2 失效 RELEASE SAVEPOINT sp1; -- 釋放保存點
MySQL 默認 REPEATABLE READ (RR),通過 MVCC + Next-Key Lock 解決幻讀。
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 鎖/ MVCC 行為 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 無鎖,讀取最新版本 |
| READ COMMITTED (RC) | ❌ | ✅ | ✅ | 每次 SELECT 生成 Read View,僅加記錄鎖 |
| REPEATABLE READ (RR) | ❌ | ❌ | ❌* | 事務啟動時生成 Read View,Next-Key Lock 防幻讀 |
| SERIALIZABLE | ❌ | ❌ | ❌ | SELECT 自動加共享鎖,退化為單線程 |
* RR 下通過 Next-Key Lock(記錄鎖+間隙鎖)實質上避免了幻讀,但極端並發仍可能出現「幻影」現象,嚴格串行化需用 SERIALIZABLE。
-- 當前會話 SELECT @@transaction_isolation; SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 全局(新連接生效) SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
LOCK TABLES、DDL、外鍵檢查、全表掃描無索引時。IS(意向共享)、IX(意向排他),協調表鎖與行鎖兼容性。-- 共享鎖 (S Lock):允許並發讀,阻塞寫 SELECT * FROM t WHERE id = 1 LOCK IN SHARE MODE; -- 排他鎖 (X Lock):阻塞讀寫 SELECT * FROM t WHERE id = 1 FOR UPDATE; UPDATE t SET ... WHERE id = 1; -- 隱式 X 鎖
| 維度 | 悲觀鎖 | 樂觀鎖 |
|---|---|---|
| 適用場景 | 衝突頻繁、寫多讀少 | 衝突稀少、讀多寫少 |
| 實現 | SELECT ... FOR UPDATE | 版本號/時間戳 + CAS 更新 |
| 優點 | 強一致性、簡單 | 無鎖開銷、高併發吞吐 |
| 缺點 | 鎖等待、死鎖風險、擴展性差 | 重試邏輯、ABA 問題 |
innodb_lock_wait_timeout(默認 50s)控制等待上限。Error 1213 (ER_LOCK_DEADLOCK) 後重試。-- 讀鎖(共享) LOCK TABLES t READ; SELECT * FROM t; UNLOCK TABLES; -- 寫鎖(排他) LOCK TABLES t WRITE; UPDATE t SET ...; UNLOCK TABLES;
缺點:並發性極差,無崩壞恢復能力,已被 InnoDB 完全取代。
ENGINE=InnoDB。@Transactional、Go tx.Begin())。LOAD DATA 繞過事務開銷。Innodb_row_lock_waits、Innodb_deadlocks、Trx_rseg_history_len(Undo 積壓)。