MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库中用于解决并发访问冲突的核心机制,广泛应用于InnoDB、PostgreSQL等主流数据库。其核心思想是为数据维护多个版本,使得读写操作可以并发执行而不互相阻塞,从而在保证事务隔离性的同时提升并发性能。
为什么需要MVCC?
传统的并发控制依赖锁机制(如行锁、表锁),但锁会导致读写冲突(例如:读操作需要等待写操作释放锁,或写操作等待读操作释放锁),在高并发场景下严重影响性能。
MVCC通过让读操作访问数据的历史版本,避免了对当前写入版本的依赖,从而实现"读不加锁、写不阻塞读",极大提升了并发效率。
MVCC的核心原理
MVCC的实现依赖三个关键组件:隐藏列 、undo日志 、Read View(读视图)。
1. 隐藏列:数据版本的"身份标识"
数据库表中的每行数据,除了用户定义的列外,还会隐含几个系统列(以InnoDB为例):
- DB_TRX_ID:记录最后一次修改该数据行的事务ID(6字节)。
- DB_ROLL_PTR:回滚指针(7字节),指向该数据行的上一个版本(存储在undo日志中)。
- DB_ROW_ID:行唯一标识(6字节),当表没有主键时,InnoDB会用它生成聚簇索引。
这些隐藏列是MVCC追踪数据版本的基础。
2. undo日志:数据版本的"历史档案"
当事务修改数据时,数据库会先将数据的旧版本写入undo日志(回滚日志),然后再更新当前数据行。
- 例如:事务T1修改了一行数据,旧版本会被存入undo日志,当前数据行的
DB_ROLL_PTR指向这个旧版本;若事务T2再次修改该行,新的旧版本(T1修改后的版本)会被存入undo日志,DB_ROLL_PTR更新为指向T2的旧版本,形成一条版本链(通过回滚指针串联的历史版本)。 - undo日志的另一个作用是事务回滚:若事务执行失败,可通过undo日志恢复数据到修改前的状态。
3. Read View:判断版本可见性的"规则"
Read View(读视图)是事务在读取数据时生成的一个"快照",用于判断当前事务能看到哪个版本的数据。它包含四个核心参数:
- m_ids:当前活跃事务的ID集合(即尚未提交的事务)。
- min_trx_id:m_ids中最小的事务ID(当前活跃事务的最小ID)。
- max_trx_id:数据库下一个将要分配的事务ID(大于当前所有活跃事务ID)。
- creator_trx_id:生成该Read View的事务自身的ID。
4. 可见性判断规则
当事务读取数据时,会通过Read View判断数据版本链中哪个版本对自己可见,规则如下(假设数据版本的DB_TRX_ID为trx_id):
- 若
trx_id == creator_trx_id:该版本是当前事务自己修改的,可见。 - 若
trx_id < min_trx_id:修改该版本的事务已提交(因为其ID小于所有活跃事务ID),可见。 - 若
trx_id >= max_trx_id:修改该版本的事务是在当前事务之后启动的,不可见。 - 若
min_trx_id <= trx_id < max_trx_id:- 若
trx_id在m_ids中(该事务仍活跃):不可见。 - 若
trx_id不在m_ids中(该事务已提交):可见。
- 若
如果当前版本不可见,事务会通过DB_ROLL_PTR回溯到上一个版本,重复判断,直到找到可见版本或版本链结束(此时返回空)。
MVCC与事务隔离级别的关系
MVCC的行为会根据事务隔离级别调整,核心差异在于Read View的生成时机:
- Read Committed(读已提交):每次执行查询时都会生成新的Read View。因此,同一事务中两次查询可能看到不同的结果(因为中间可能有其他事务提交)。
- Repeatable Read(可重复读,InnoDB默认):仅在事务第一次执行查询时生成Read View,后续查询复用该视图。因此,同一事务中多次查询看到的结果一致(避免了不可重复读)。
(注:Serializable隔离级别通常不依赖MVCC,而是通过加锁实现;Read Uncommitted直接读取最新版本,不适用MVCC。)
MVCC的优势
- 读写不冲突:读操作无需加锁,直接访问历史版本;写操作仅锁定当前版本,不阻塞读,极大提升并发性能。
- 简化隔离级别实现:通过Read View的生成时机和可见性规则,自然实现了Read Committed和Repeatable Read隔离级别的语义。
- 避免锁竞争:减少了传统锁机制的阻塞和等待,降低了死锁风险。
总结
MVCC通过隐藏列记录版本标识 、undo日志维护历史版本链 、Read View判断可见性,实现了"多版本并发访问"。其核心价值是在保证事务隔离性的前提下,最大化读写并发效率,是现代数据库高性能的关键机制之一。