MySQL事务与MVCC完全指南:从ACID到隔离级别,一文读懂多版本并发控制
MySQL事务与MVCC完全指南:从ACID到隔离级别,一文读懂多版本并发控制
事务是数据库的核心特性,而MySQL的InnoDB通过MVCC(多版本并发控制)实现了高并发与数据一致性的优雅平衡。本文从ACID四大特性讲起,深入剖析事务并发带来的三大问题,图解四种隔离级别,最后彻底解密MVCC的底层原理与Read View判断逻辑,助你掌握MySQL事务的精髓。
一、什么是数据库事务?
事务是数据库管理系统执行过程中的一个逻辑单元,由一组有限的数据库操作序列构成。比如银行转账:从A账户扣款100元,向B账户加款100元,这两步必须要么全部成功,要么全部失败——这就是事务的典型场景。
BEGIN; UPDATE account SET balance = balance - 100 WHERE id = 1; UPDATE account SET balance = balance + 100 WHERE id = 2; COMMIT; -- 或 ROLLBACK;
二、事务的ACID四大特性
| 特性 | 含义 | InnoDB实现方式 |
|---|---|---|
| 原子性(Atomicity) | 事务中的所有操作要么全部成功,要么全部失败回滚 | Undo Log(撤销日志) |
| 一致性(Consistency) | 事务执行前后,数据库从一个一致状态变到另一个一致状态 | 通过原子性、隔离性、持久性共同保证 |
| 隔离性(Isolation) | 一个事务的执行不被其他事务干扰 | MVCC + 锁机制 |
| 持久性(Durability) | 一旦事务提交,其修改永久保存,即使系统崩溃也不丢失 | Redo Log(重做日志) + 双写缓冲区 |
三、事务并发带来的三大问题
当多个事务同时执行而未做好隔离时,会产生以下问题:
1. 脏读(Dirty Read)
一个事务读取到另一个未提交事务修改的数据。如果该事务最终回滚,读到的数据就是无效的“脏数据”。

2. 不可重复读(Non-Repeatable Read)
同一个事务中,两次相同的查询返回了不同的结果,因为其他事务在此期间修改并提交了数据。
3. 幻读(Phantom Read)
同一个事务中,两次相同的范围查询返回的记录数不同,因为其他事务在此期间插入或删除了符合条件的数据。
小结:不可重复读侧重“修改”,幻读侧重“新增或删除”。
四、SQL标准定义的四种隔离级别
不同的隔离级别解决了上述问题的不同程度
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED(读未提交) | 可能 | 可能 | 可能 |
| READ COMMITTED(读已提交,RC) | 不可能 | 可能 | 可能 |
| REPEATABLE READ(可重复读,RR) | 不可能 | 不可能 | 可能(InnoDB通过间隙锁解决) |
| SERIALIZABLE(串行化) | 不可能 | 不可能 | 不可能 |
-
READ UNCOMMITTED:几乎不用,会导致脏读。
-
READ COMMITTED:Oracle等默认级别,避免脏读,但可能不可重复读。
-
REPEATABLE READ:MySQL InnoDB默认级别,避免了脏读和不可重复读,并通过间隙锁解决了幻读。
-
SERIALIZABLE:最高隔离级别,所有事务串行执行,性能极差。

五、MVCC:无锁的“快照读”如何工作?
MVCC(Multi-Version Concurrency Control,多版本并发控制)是InnoDB实现高并发一致性读的核心机制。它让读操作不阻塞写操作,写操作也不阻塞读操作,极大提升了并发性能。
1. MVCC的三大依赖组件
| 组件 | 作用 |
|---|---|
| 隐藏字段 | 每行记录包含 DB_TRX_ID(最后修改事务ID)、DB_ROLL_PTR(回滚指针,指向Undo Log)等 |
| Undo Log | 记录每次修改前的旧版本,形成版本链 |
| Read View | 事务进行快照读时,决定能看到哪些版本的快照 |
2. 隐藏字段与版本链
每行记录有三个隐藏列:
-
DB_ROW_ID(6字节):隐式自增ID,当表无主键时自动生成。 -
DB_TRX_ID(6字节):最后一次修改该行的事务ID。 -
DB_ROLL_PTR(7字节):回滚指针,指向Undo Log中的上一个版本。
当更新一行时,旧版本被写入Undo Log,新记录的回滚指针指向它,形成版本链。

3. Read View:可见性判断的核心
Read View在快照读(普通SELECT)时生成,内部包含:
-
m_ids:生成Read View时活跃(未提交)的事务ID列表。 -
min_trx_id:m_ids中的最小值。 -
max_trx_id:下一个将要分配的事务ID。 -
creator_trx_id:当前事务自己的ID。
判断规则(以记录的事务ID trx_id 为参考):
-
如果
trx_id<min_trx_id→ 事务已提交,可见。 -
如果
trx_id>=max_trx_id→ 事务在Read View之后创建,不可见。 -
如果
trx_id在m_ids中(且不是自己)→ 事务未提交,不可见。 -
否则(
trx_id不在m_ids中,且小于max_trx_id)→ 事务已提交,可见。
简单记:只能看见“快照生成时已提交”的事务或自己修改的数据。
4. RC与RR下Read View的差异
-
READ COMMITTED:每次执行快照读,都生成一个新的Read View。因此可以看到其他事务已提交的新数据(可能产生不可重复读)。
-
REPEATABLE READ:第一次快照读时生成Read View,整个事务复用同一个Read View。因此多次读取始终一致(可重复读),加上间隙锁,彻底避免了幻读。

浙公网安备 33010602011771号