事务隔离级别有哪几种?脏读、不可重复读、幻读分别是什么?
结论先行:SQL 标准定义了 4 个隔离级别:读未提交、读已提交、可重复读、串行化,级别从低到高,隔离性递增、并发性递减。三个并发读问题:脏读是读到别人“未提交”的数据;不可重复读是同一行两次读结果不同;幻读是同一条件两次查询结果集“行数”不同。
隔离级别与问题对照表
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(Read Uncommitted) | 可能 | 可能 | 可能 |
| 读已提交(Read Committed) | 不可能 | 可能 | 可能 |
| 可重复读(Repeatable Read) | 不可能 | 不可能 | 基本不可能(InnoDB 默认) |
| 串行化(Serializable) | 不可能 | 不可能 | 不可能 |
补充:InnoDB 默认可重复读,通过 MVCC + 间隙锁把幻读也基本消除,实际能力比标准同名级别更强;Oracle、PostgreSQL 默认则是读已提交。
三种问题示例
-- 1) 脏读:事务 A 更新未提交,事务 B 读到新值,A 回滚 → B 读到脏数据
UPDATE account SET balance = 0 WHERE id = 1; -- A 未提交
-- 2) 不可重复读:事务 B 两次读同一行,结果不同
SELECT balance FROM account WHERE id = 1; -- B 第一次读到 100
-- (A 提交 balance=0 之后)
SELECT balance FROM account WHERE id = 1; -- B 第二次读到 0
-- 3) 幻读:事务 B 两次范围查询,行数不同
SELECT COUNT(*) FROM account WHERE balance < 100; -- B 第一次查到 1 行
-- (A 插入新行并提交)
SELECT COUNT(*) FROM account WHERE balance < 100; -- B 第二次查到 2 行
常见追问 / 记忆点
- 追问:MySQL 默认隔离级别是什么?答:可重复读;而 Oracle、PostgreSQL 默认读已提交。
- 追问:读已提交与可重复读在实现上差在哪?答:读已提交每次 SELECT 重新生成 Read View,可重复读只在事务内第一次快照读时生成。
- 记忆点:脏读看“别人没提交的”,不可重复读看“同一行变了”,幻读看“多了一行/少了一行”。