1. MVCC 的核心思想
MVCC 的核心思想是为每条记录维护多个版本,每个事务在读取数据时,只能看到在它开始之前已经提交的数据版本,而不会看到未提交的数据或其他事务的修改。这样可以避免读写冲突,提高并发性能。
2. MVCC 的实现原理
MySQL 的 MVCC 主要通过以下机制实现:
1. 隐藏字段
InnoDB 表中每行记录都有两个隐藏字段:
- DB_TRX_ID:记录最后一次修改该行数据的事务 ID。
- DB_ROLL_PTR:指向该行数据的 undo log(回滚日志)指针,用于找到历史版本。
2. Undo Log(回滚日志)
- Undo Log 用于存储数据的历史版本。
- 当事务更新某行数据时,会将旧数据写入 Undo Log,并通过 DB_ROLL_PTR 指向它。
- 通过 Undo Log,可以构建出数据的历史版本。
3. Read View(读视图)
-
每个事务在开始时都会生成一个 Read View,用于决定当前事务能看到哪些数据版本。
-
Read View 包含以下信息:
m_ids:当前活跃(未提交)的事务 ID 列表。min_trx_id:m_ids中的最小事务 ID。max_trx_id:下一个即将分配的事务 ID。creator_trx_id:创建该 Read View 的事务 ID。
3. MVCC 的工作流程
1. 数据读取
-
当事务读取某行数据时,会检查该行的 DB_TRX_ID:
- 如果 DB_TRX_ID 小于
min_trx_id,说明该行数据在事务开始前已提交,可见。 - 如果 DB_TRX_ID 大于等于
max_trx_id,说明该行数据在事务开始后修改,不可见。 - 如果 DB_TRX_ID 在
m_ids中,说明该行数据由未提交的事务修改,不可见;否则可见。
- 如果 DB_TRX_ID 小于
-
如果当前版本不可见,则通过 DB_ROLL_PTR 找到 Undo Log 中的历史版本,直到找到可见的版本。
2. 数据写入
- 当事务更新某行数据时,会生成一个新的版本,并将旧数据写入 Undo Log。
- 新版本的 DB_TRX_ID 设置为当前事务 ID,DB_ROLL_PTR 指向旧版本。
3. 数据删除
- 删除操作实际上是标记删除,不会立即删除数据。
- 被删除的数据会写入 Undo Log,直到没有事务需要访问该数据时才会真正删除。
4. MVCC 的优点
- 高并发:读写操作不会互相阻塞,提高了并发性能。
- 一致性读:事务可以看到一致的数据快照,避免了脏读、不可重复读和幻读。
- 回滚支持:通过 Undo Log 可以轻松实现事务回滚。
5. MVCC 的缺点
- 存储开销:需要维护多个数据版本,增加了存储空间。
- 清理成本:需要定期清理不再需要的 Undo Log 和历史版本数据(通过 Purge 线程)。
6. MVCC 与隔离级别
MVCC 的行为与事务的隔离级别密切相关:
- 读未提交(Read Uncommitted) :不使用 MVCC,可能读到未提交的数据。
- 读已提交(Read Committed) :每次读取时生成新的 Read View,只能看到已提交的数据。
- 可重复读(Repeatable Read) :事务开始时生成一个 Read View,整个事务期间都使用该视图。
- 串行化(Serializable) :通过加锁实现,不使用 MVCC。
7. 示例
假设有以下事务操作:
-
事务 A(ID=100)插入一行数据,DB_TRX_ID=100。
-
事务 B(ID=101)更新该行数据,DB_TRX_ID=101,旧数据写入 Undo Log。
-
事务 C(ID=102)读取该行数据:
- 如果事务 C 的隔离级别是 Read Committed,它会看到事务 B 提交后的最新版本。
- 如果事务 C 的隔离级别是 Repeatable Read,它会看到事务 A 提交的版本(事务开始时生成 Read View)。 。