MVCC(Multi-Version Concurrency Control,多版本并发控制) 是一种用于数据库管理系统(如 MySQL InnoDB 引擎)实现高并发读写操作的机制。MVCC 通过为每个事务提供一个数据的“快照”,使得读取操作可以与写入操作并发进行,而不会互相阻塞。它能够提高数据库的并发性能,同时保证数据的一致性和隔离性。
MVCC 的工作原理
MVCC 的核心思想是:在同一时间内,允许多个事务对同一数据进行并发访问,而每个事务都能看到一个数据的“快照”(数据的一个版本)。数据库使用版本号或时间戳来区分和管理这些不同的版本。
在 MVCC 下,数据库会对每行数据保存多个版本(版本化数据),每个版本都有特定的元数据:
- 创建版本(事务 ID 或时间戳):标记一个版本在什么时间点或由哪个事务创建。
- 删除版本(事务 ID 或时间戳):标记一个版本在什么时间点或由哪个事务删除。
- 数据内容:每个版本的实际数据。
1. MVCC 处理读操作
当事务执行读操作时,MVCC 根据该事务的“快照”读取满足条件的版本:
- 读已提交(Read Committed):事务总是读取已提交的数据版本(最新版本),在当前事务进行的每次读取时,都重新生成一个数据快照。
- 可重复读(Repeatable Read):在事务开始时创建一个一致性快照,所有的读取操作都基于该快照,无论其他事务是否对数据进行了修改或插入,都不会影响当前事务的读取结果。
2. MVCC 处理写操作
当事务进行写操作时(插入、更新或删除),会创建数据的新版本并标记版本号或时间戳:
- 插入数据时:生成一个新的数据版本,并赋予该事务的版本号。
- 更新数据时:将现有的数据版本标记为“删除”,同时插入一个新版本的数据。
- 删除数据时:将现有的数据版本标记为“删除”。
MVCC 在 MySQL InnoDB 中的实现
在 MySQL InnoDB 存储引擎中,MVCC 的实现依赖于undo log(回滚日志)和隐藏列:
-
Undo Log:存储每个事务操作之前的数据快照。当事务需要回滚时,可以使用 undo log 恢复到原始数据状态。MVCC 也使用 undo log 为每个读操作提供一致性视图。
-
隐藏列:InnoDB 表的每行数据实际上都包含两个隐藏列:
- DB_TRX_ID:表示最后一次插入或更新操作的事务 ID。
- DB_ROLL_PTR:指向 undo log 中对应的回滚日志,记录该行数据的历史版本。
当一个事务读取数据时,InnoDB 会根据事务的隔离级别和快照时间来决定应该返回哪个版本的数据。
MVCC 如何解决并发问题
- 避免脏读:MVCC 确保事务只能看到其他事务已提交的修改或快照中存在的数据。
- 避免不可重复读:在
REPEATABLE READ
隔离级别下,每个事务在读取数据时始终基于开始时的快照,因此不会读取到其他事务提交后的修改。 - 避免幻读:通过 MVCC 提供的快照,配合
Next-Key Locking
机制,确保范围查询的一致性,防止插入新行引起的幻读。
MVCC 的优势
- 高并发性能:读操作不加锁,不会阻塞其他读操作或写操作,从而提高并发性能。
- 避免锁争用:读操作不会与写操作冲突,这减少了锁争用的可能性,提高了系统的吞吐量。
MVCC 的缺点
- 存储开销:由于需要保存多个版本的数据,会带来一定的存储开销。
- 管理开销:系统需要维护多个版本的数据,并清理过期版本,这增加了系统的复杂性和管理成本。
总结
MVCC 通过为每个事务提供数据的多版本快照,使得读操作和写操作可以并发进行,有效地解决了数据库的并发控制问题,提高了系统性能和数据一致性。在 MySQL InnoDB 中,MVCC 结合了回滚日志(undo log)和隐藏列来实现数据的多版本控制。