事务的 ACID 如何理解?MySQL 的隔离级别与实现手段是什么?
结论先行:ACID 是事务正确性的四个性质:原子性、一致性、隔离性、持久性。InnoDB 的实现分工清晰:原子性靠 undo log,持久性靠 redo log,隔离性靠 MVCC + 锁,一致性靠约束与前三者共同保证;MySQL 默认隔离级别是可重复读(RR)。
一、ACID 与对应实现
| 性质 | 含义 | InnoDB 靠什么保证 |
|---|
| A 原子性 | 要么全成功要么全回滚 | undo log:失败时撤销已做修改 |
| C 一致性 | 数据始终满足约束与业务规则 | 约束 + AID 共同作用,应用层也参与 |
| I 隔离性 | 并发事务互不干扰 | MVCC 快照读 + 锁(当前读) |
| D 持久性 | 提交后不丢失 | redo log 预写 + 刷盘 |
二、四种隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|
| 读未提交 RU | 可能 | 可能 | 可能 | 基本不用 |
| 读已提交 RC | 不会 | 可能 | 可能 | Oracle 默认 |
| 可重复读 RR | 不会 | 不会 | 可能(可被间隙锁避免) | MySQL 默认 |
| 串行化 | 不会 | 不会 | 不会 | 加锁范围大,性能低 |
- 脏读:读到其它未提交事务的数据。
- 不可重复读:同一事务两次读同一条记录结果不同(针对行更新)。
- 幻读:同一事务两次范围查询多出/少了行(针对插入)。
三、MVCC、快照读与当前读
- 快照读(普通 SELECT):走 undo 版本链 + 读视图,不加锁;RR 下整个事务用同一份快照。
- 当前读(UPDATE/DELETE/SELECT ... FOR UPDATE):读最新版本并加锁。
- RR 下当前读用 next-key lock(行锁 + 间隙锁)封住范围,从机制上消除幻读。
四、事务语句与加锁快查
| 语句 | 读类型 | 加锁情况 |
|---|
| SELECT(普通查询) | 快照读 | 不加锁 |
| SELECT ... FOR UPDATE | 当前读 | 行锁 + 间隙锁 |
| UPDATE / DELETE | 当前读 | 行锁 + 间隙锁 |
常见追问与记忆点
- 追问:为什么 MySQL 默认 RR 而不是 RC?历史兼容:早期 statement 格式 binlog 下 RC 会主从不一致,RR 对开发者也更安全。
- 追问:大厂为什么常改成 RC?并发写冲突少、间隙锁开销低,配合 row 格式 binlog 无主从不一致风险。
- 记忆点:原子靠 undo、持久靠 redo、隔离靠 MVCC + 锁;隔离级别读问题从 RU 到串行化逐级变少。