MVCC看似简单,实则简单

77 阅读3分钟

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_idm_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_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. 示例

假设有以下事务操作:

  1. 事务 A(ID=100)插入一行数据,DB_TRX_ID=100。

  2. 事务 B(ID=101)更新该行数据,DB_TRX_ID=101,旧数据写入 Undo Log。

  3. 事务 C(ID=102)读取该行数据:

    • 如果事务 C 的隔离级别是 Read Committed,它会看到事务 B 提交后的最新版本。
    • 如果事务 C 的隔离级别是 Repeatable Read,它会看到事务 A 提交的版本(事务开始时生成 Read View)。 。