欢迎来到窥视未来的博客

https://github.com/lwx57280 https://gitee.com/li_VillageHead

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)

一个事务读取到另一个未提交事务修改的数据。如果该事务最终回滚,读到的数据就是无效的“脏数据”。

image

 

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:最高隔离级别,所有事务串行执行,性能极差。

 

image

 

五、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,新记录的回滚指针指向它,形成版本链。

 

image

 

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 为参考):

  1. 如果 trx_id < min_trx_id → 事务已提交,可见。

  2. 如果 trx_id >= max_trx_id → 事务在Read View之后创建,不可见。

  3. 如果 trx_id 在 m_ids 中(且不是自己)→ 事务未提交,不可见。

  4. 否则(trx_id 不在 m_ids 中,且小于 max_trx_id)→ 事务已提交,可见。

简单记:只能看见“快照生成时已提交”的事务或自己修改的数据。

4. RC与RR下Read View的差异

  • READ COMMITTED:每次执行快照读,都生成一个新的Read View。因此可以看到其他事务已提交的新数据(可能产生不可重复读)。

  • REPEATABLE READ:第一次快照读时生成Read View,整个事务复用同一个Read View。因此多次读取始终一致(可重复读),加上间隙锁,彻底避免了幻读。

六、MVCC解决了什么?没解决什么?

 
问题MVCC是否解决说明
脏读 ✅ 通过Read View过滤未提交事务
不可重复读 ✅(RR级别) 通过复用Read View
幻读 ✅(RR级别,配合间隙锁) 快照读无幻读,当前读依赖间隙锁
更新丢失(Lost Update) ❌ MVCC不解决写-写冲突,需应用锁或乐观锁

七、一张图总结InnoDB事务体系

 

image

 


八、实战建议

  1. 默认使用REPEATABLE READ:InnoDB的RR级别性能良好且解决了幻读,适合绝大多数业务。

  2. 读多写少且对一致性要求不高时,可降级为RC:RC无间隙锁,并发更高,但可能产生不可重复读和幻读。

  3. 理解快照读与当前读:

    • 快照读:普通SELECT,走MVCC,不加锁。

    • 当前读:SELECT ... FOR UPDATE、UPDATE、DELETE,读取最新版本并加锁。

  4. 长事务危害:持有Read View太久会导致Undo Log无法清理,影响性能。


总结

  • 事务的ACID特性由Undo Log、Redo Log、MVCC和锁共同保证。

  • MVCC通过隐藏字段、Undo版本链和Read View实现了无锁的一致性快照读,是InnoDB高并发的基石。

  • 不同隔离级别下Read View的生成时机不同,导致了可见性的差异。

  • RR级别+间隙锁完全解决了脏读、不可重复读和幻读问题。

如果觉得本文对你有帮助,欢迎点赞、收藏、转发!
关注我,获取更多MySQL内核深度内容。

 
 

posted on 2026-05-09 16:26  k8s-Mango  阅读(73)  评论(0)    收藏  举报

导航