MySQL 事務隔離級別深度解析
一、鎖類型與粒度
| 鎖類型 | 特點 | 適用引擎 |
| 表鎖 | 開銷最小、並發最低、鎖定整張表 | MyISAM、DDL 操作 |
| 行鎖 | 開銷最大、並發最高、僅鎖相關行 | InnoDB、Falcon |
行鎖僅在存儲引擎層實現,MySQL Server 層感知不到;InnoDB 預設行鎖,支援間隙鎖、Next-Key Lock 解決幻讀。
二、ACID 原則與銀行轉賬示例
BEGIN;
SELECT balance FROM checking WHERE customer_id = 10233276;
UPDATE checking SET balance = balance - 200 WHERE customer_id = 10233276;
UPDATE savings SET balance = balance + 200 WHERE customer_id = 10233276;
COMMIT;
- 原子性:要麼全成功要麼全回滾
- 一致性:轉賬前後總金額不變
- 隔離性:A 未提交前,B 查詢看到舊餘額
- 持久性:Commit 後掉電不丟數據
三、五大併發異常現象
| 異常 | 描述 | 例子 |
| 丟失更新 | 回滾覆蓋他人已提交更新 | A提交→B更新→B回滾→A更新丟失 |
| 髒讀 | 讀到未提交數據 | B更新未提交→A讀到→B回滾→A得髒數據 |
| 不可重複讀 | 同一事務兩次讀同行,結果不同 | A讀→B更新提交→A再讀→值變了 |
| 覆蓋更新 | 不可重複讀特例 | A更新→B更新→A發現自己更新被覆蓋 |
| 幻讀 | 同一事務兩次範圍查詢,行數不同 | A查範圍→B插入新行→A再查→多出行 |
四、四大隔離級別對比
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 備註 |
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 無共享鎖,性能最高但最不安全 |
| READ COMMITTED | 不可能 | 可能 | 可能 | 讀取瞬間加共享鎖,讀完即釋放(Oracle 預設) |
| REPEATABLE READ | 不可能 | 不可能 | 可能 | InnoDB 預設,MVCC 快照讀解決不可重複讀 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 | 全表共享鎖,串行化,並發極低 |
五、實戰演示:RR 隔離級別下的幻讀、RC 下的不可重複讀、RU 下的髒讀
準備:建表與數據
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32), age TINYINT,
KEY idx_age (age)
) ENGINE=InnoDB;
INSERT INTO users VALUES
(1,'Bob',27),(2,'Mike',7),(3,'Tony',40),(4,'Bill',21),(5,'Mark',18);
1. 幻讀
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 事務1
BEGIN;
SELECT * FROM users WHERE age BETWEEN 10 AND 30; -- 返回 Bob, Mike, Bill, Mark
-- 事務2
INSERT INTO users VALUES (6,'New',25); COMMIT;
-- 事務1 再次查詢
SELECT * FROM users WHERE age BETWEEN 10 AND 30; -- RR 下仍只見 4 行,無幻讀(靠 Gap Lock)
2. 不可重複讀
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 事務1
BEGIN;
SELECT * FROM users WHERE id = 1; -- age=27
-- 事務2
UPDATE users SET age = 21 WHERE id = 1; COMMIT;
-- 事務1 再次查詢
SELECT * FROM users WHERE id = 1; -- age=21,發生不可重複讀
3. 髒讀
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 事務1
BEGIN;
SELECT * FROM users WHERE id = 1;
-- 事務2
UPDATE users SET age = 21 WHERE id = 1; -- 未提交
-- 事務1 再次查詢
SELECT * FROM users WHERE id = 1; -- 讀到 age=21(髒數據)
-- 事務2 ROLLBACK; 事務1 讀到的數據已失效
六、InnoDB MVCC 實現細節
- 每行隱藏
DB_TRX_ID(創建版本)、DB_ROLL_PTR(回滾指鏈)
- RR 下:
SELECT 讀取「創建版本 ≤ 當前事務版本 且 (刪除版本為空 OR > 當前版本)」的行 → 快照讀,不加鎖
LOCK IN SHARE MODE / FOR UPDATE / DML → 當前讀,加 S/X 鎖,讀最新版本
七、binlog 格式與隔離級別的關係
STATEMENT:需 SERIALIZABLE 保證主備一致
ROW / MIXED:READ COMMITTED 以上即可,推薦生產用 ROW + READ COMMITTED 平衡效能與一致性
八、總結
MySQL InnoDB 預設 RR + MVCC 解決不可重複讀,配合 Next-Key Lock 緩解幻讀;高併發業務常用 RC + ROW binlog 提升並發;極少數強一致場景用 Serializable。理解五大異常、四大級別、MVCC 可見性判斷、鎖類型,是調優與排查併發 Bug 的核心基石。