一、什么是MVCC?
MySQL中的MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种提高数据库并发性能的机制,主要用于解决读写冲突,实现非阻塞的读操作。它是InnoDB存储引擎实现事务隔离级别(尤其是REPEATABLE READ和READ COMMITTED)的核心技术。
二、MVCC主要用途
MVCC 通过保存数据的多个版本,让读操作可以读取历史版本的数据。
1.解决并发冲突
- 读写不阻塞:读操作读取快照数据,不影响写操作更新当前数据。
- 写写冲突:通过行锁(Record Lock)+ 间隙锁(Gap Lock)处理。
2.提高并发性能
- 避免了大量的锁竞争
- 非阻塞的读操作特别适合读多写少的场景。
3.支持事务隔离级别
不同隔离级别下提供不同的一致性保证。
- READ COMMITTED:每次读取都生成新的ReadView
- REPEATABLE READ:第一次读取时生成ReadView,整个事务期间复用
注:MySQL的RR级别通过MVCC+Next-Key Lock基本解决了幻读问题
三、MVCC底层原理(InnoDB引擎)
MySQL的MVCC通过隐藏字段、Undo Log版本链、ReadView的巧妙组合,实现了高效的非锁定读。
InnoDB 通过以下技术实现 MVCC:
1.隐藏字段
InnoDB为每行记录添加了三个隐藏字段:
- DB_TRX_ID:6字节,最后一次修改该行的事务ID。
- DB_ROLL_PTR:7字节,回滚指针,指向该行的上一个版本(存储在undo log中)。
- DB_ROW_ID:6字节,隐含的行字增ID(当没有主键时自动生成)。
2.Undo Log(回滚日志)
- 存储数据的历史版本(通过DB_ROLL_PTR形成版本链)。
- 用于事务回滚、一致性读、崩溃恢复。
3.Read View(读视图)
- 事务在执行快照读(如SELECT)时,会生成一个Read View,用于判断当前事务能看到哪个版本的数据。
- Read View 包含:
- creator_trx_id:创建该Read View的事务ID。
- trx_ids:未提交事务的id列表。生成ReadView时活跃的事务ID列表,即当前活跃(未提交)的事务ID列表。
- min_trx_id:活跃事务(trx_ids)中最小的事务ID。
- max_trx_id:系统将分配给下一个事务的ID,即read view生成时刻系统尚未分配的下一个事务ID。
四、MVCC工作流程
1. 数据版本链的构建
java
版本链示例:
当前行记录 ← undo log v3 ← undo log v2 ← undo log v1
↑ ↑ ↑ ↑
事务T4 事务T3 事务T2 事务T1
(当前版本) (通过roll_ptr) (通过roll_ptr) (初始版本)2.数据读取时的版本可见性判断
当执行SELECT时,对于每一行数据,会遍历数据行的版本链(通过DB_ROLL_PTR),按以下规则顺序判断是否可见:
- 1.如果版本的事务ID等于read_view.creator_trx_id,说明是自己修改的,可见。
- 2.如果版本的事务ID小于read_view.min_trx_id,说明该版本已提交(事务在ReadView创建前已提交),可见。
- 3.如果版本的事务ID 大于等于read_view.max_trx_id,说明该版本由未来事务创建(事务在ReadView创建后开始),不可见。
- 4.如果版本的事务ID在 trx_ids 中,说明该版本由未提交事务创建(事务在ReadView创建时仍活跃),不可见。
- 5.如果版本的事务ID在 min_trx_id 和 max_trx_id 之间,且不在活跃事务列表中,说明已提交,可见。
3.不同隔离级别的实现差异
- READ COMMITTED:每次SELECT都会生成新的Read View,能看到其他事务已提交的最新数据。不能解决幻读。
- REPEATABLE READ:第一次SELECT时生成Read View,后续读取都使用该View,保证可重复读。基本解决幻读。
五、MVCC与锁的协同工作
1. 当前读 vs 快照读
快照读: 不像加锁的select操作就是快照读,即不加锁的非阻塞读。快照读的前提是隔离级别不是串行化,串行化级别下快照读退化成当前读。快照读的实现是基于mvcc。 当前读:
- 1.select xx lock in share mode(共享锁);
- 2.select xx for update、insert、update、delete (排他锁);
以上这些操作都是一种当前读,即它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
sql
-- 快照读(使用MVCC)
SELECT * FROM table; -- 普通SELECT
-- 当前读(使用锁)
SELECT * FROM table LOCK IN SHARE MODE; -- 加共享锁
SELECT * FROM table FOR UPDATE; -- 加排他锁
UPDATE/DELETE/INSERT; -- 总是当前读2. Next-Key Lock防止幻读
在RR级别下,MVCC配合Next-Key Lock:
- 快照读:通过MVCC避免幻读。
- 当前读:通过Next-Key Lock(记录锁+间隙锁)避免幻读。
六、MVCC的优缺点
优点:
- 高并发:读写不相互阻塞。
- 避免死锁:读操作不需要加锁。
- 一致性读:提供时间点的一致性快照。
- 避免幻读(在RR级别下通过MVCC + Next-Key Lock解决)。
- 回滚高效:直接通过undo log回滚到历史版本。
缺点: - 额外存储:需要维护undo log和版本链。
- 清理开销:依赖后台purge线程清理不再需要的undo log。
- 历史数据:长时间事务可能阻止旧版本清理。
