MySQL中多版本并发控制(MVCC, Multi-Version Concurrency Control)是一种旨在提高数据库并发性能的机制,特别适用于InnoDB存储引擎。MVCC通过维护数据的多个版本来实现事务间的并发访问,允许读操作与写操作在大多数情况下无需相互阻塞,从而提升了数据库系统的并发能力和整体性能。
1. 数据版本化
隐藏列与事务ID
InnoDB为每行数据添加了几个隐藏列,其中与MVCC相关的主要是事务ID(Transaction ID)和 回滚指针(Roll Pointer)。事务ID记录了最后一次修改该行数据的事务的ID,而回滚指针指向存储在Undo Log中的该行数据的前一个版本。
Undo Log
每当事务对数据进行修改(INSERT、UPDATE、DELETE)时,InnoDB不仅会更新当前数据,还会在Undo Log中记录修改前的旧版本。Undo Log按事务顺序组织,形成了一个版本链,用于在需要时回滚事务或提供历史版本供其他事务读取。
2. 事务视图与快照读
事务视图
每个事务在启动时会获得一个唯一的事务ID,并基于这个ID构建一个事务视图。事务视图决定了事务能看到哪些数据版本:
- 对于已提交事务,事务视图包含所有在其启动前已提交的事务所修改的数据版本。
- 对于未提交事务,即使它们修改的数据已写入到数据页,事务视图也不包含这些未提交的更改。
快照读与当前读
根据读取数据的方式,MySQL区分两种类型的读操作:
- 快照读(Snapshot Read):这是MVCC中的非锁定读,包括普通的SELECT查询(不使用
FOR UPDATE
或LOCK IN SHARE MODE
)以及内部的索引查找。快照读会根据事务视图返回对应版本的数据,不会阻塞其他事务的写操作。 - 当前读(Current Read):包括SELECT … FOR UPDATE、SELECT … LOCK IN SHARE MODE、INSERT、UPDATE、DELETE等操作,这些操作会获取数据的最新版本(可能需要等待其他事务释放锁)并确保后续的修改不会被其他事务覆盖。当前读会使用锁来实现隔离性,与MVCC的非锁定读相对。
3. 版本选择与清理
版本选择
当事务进行快照读时,InnoDB会根据事务视图找到符合要求的版本:
- 如果一行数据的事务ID小于等于事务视图中的最小已提交事务ID,那么该行数据对于当前事务可见。
- 如果一行数据的事务ID大于事务视图中的最小已提交事务ID,且该行处于未提交状态(即由其他未提交事务创建或修改),那么该行对当前事务不可见。
版本清理
随着事务的提交和回滚,旧版本的数据会逐渐积累。InnoDB通过purge操作来清理不再需要的旧版本数据,以释放空间。Purge操作通常由后台线程异步执行,确保不影响正常的数据库操作。
4. 隔离级别与MVCC的关系
MVCC在不同的隔离级别下表现不同:
- 在
READ COMMITTED
隔离级别,每次快照读都会获取最新的已提交数据版本,因此每次查询可能看到不同的结果。 - 在
REPEATABLE READ
隔离级别(InnoDB默认级别),整个事务期间的所有快照读都将看到同一版本的数据,保证了多次查询结果的一致性。InnoDB通过Next-Key Locking和MVCC结合,避免了幻读问题。