一、什么是MVCC?

想象一下图书馆借书的场景:当一本书被借走时,其他人仍然可以查看这本书的信息,只是不能借出。数据库中的 MVCC(Multi-Version Concurrency Control,多版本并发控制)机制也是类似的思路——它让读写操作可以同时进行,而不会互相阻塞。

MVCC 是 InnoDB 存储引擎实现高并发的重要机制。它的核心思想是:每个事务看到的数据版本可能不同,而不是直接锁定数据。这样,读操作不会阻塞写操作,写操作也不会阻塞读操作,从而提升数据库的并发性能。

二、MVCC 的核心实现原理

MVCC 的实现依赖于三个关键机制:

  1. 隐藏字段
    InnoDB 在每行记录中隐藏存储了三个字段:

    • DB_TRX_ID:记录最后一次修改该行的事务 ID。
    • DB_ROLL_PTR:指向该行的 undo log(回滚日志)指针,用于找到旧版本数据。
    • DB_ROW_ID:行 ID(如果没有主键,InnoDB 会自动生成)。
  2. ReadView(读视图)
    事务在读取数据时,会生成一个 ReadView,包含:

    • m_ids:当前活跃(未提交)的事务 ID 列表。
    • min_trx_id:当前活跃事务中的最小事务 ID。
    • max_trx_id:下一个即将分配的事务 ID。
    • creator_trx_id:创建该 ReadView 的事务 ID。
  3. Undo Log(回滚日志)
    当事务修改数据时,InnoDB 会先将旧数据写入 undo log,再修改内存中的数据。如果事务回滚,可以通过 undo log 恢复数据。

三、MVCC 如何判断数据可见性

当一个事务执行查询时,InnoDB 会按照以下规则判断某行数据是否可见:

  1. 如果该行的 DB_TRX_ID 小于 min_trx_id,说明该行在事务开始前就已提交,可见。
  2. 如果 DB_TRX_ID 大于等于 max_trx_id,说明该行是未来事务修改的,不可见。
  3. 如果 DB_TRX_IDm_ids 中,说明该行由未提交事务修改,不可见。
  4. 如果 DB_TRX_ID 等于 creator_trx_id,说明该行由当前事务修改,可见。

示例(MySQL 8.0+):

-- 事务1:修改数据
START TRANSACTION;
UPDATE users SET name = 'Alice' WHERE id = 1;
-- 此时事务未提交,其他事务看不到这个修改

-- 事务2:查询数据
START TRANSACTION;
SELECT name FROM users WHERE id = 1;  -- 看到的是旧数据
COMMIT;

-- 事务1提交后,其他事务才能看到新数据
COMMIT;

四、MVCC 的应用场景

  1. 读多写少的业务
    例如电商的商品浏览、新闻网站的阅读,MVCC 可以让大量读操作不阻塞写操作。

  2. 长事务与短事务共存
    报表查询(长事务)不会阻塞订单处理(短事务)。

  3. 避免幻读(在 REPEATABLE READ 隔离级别下)
    InnoDB 通过 MVCC + 间隙锁(Gap Lock)防止幻读。

五、MVCC 的优缺点

优点:

  • 读写不冲突,提高并发性能。
  • 避免脏读、不可重复读(取决于隔离级别)。

缺点:

  • 需要额外存储 undo log,占用空间。
  • 长时间运行的事务可能导致 undo log 堆积,影响性能。

六、注意事项

  1. 避免长事务
    长时间不提交的事务会阻止 undo log 清理,可能导致数据库膨胀。

  2. 合理设置隔离级别

    • READ COMMITTED:每次查询生成新 ReadView,可能不可重复读。
    • REPEATABLE READ(默认):事务内使用同一个 ReadView,保证可重复读。
  3. 监控 undo log 使用情况

    SHOW ENGINE INNODB STATUS\G  -- 查看 undo log 信息
    

七、总结

MVCC 是 InnoDB 实现高并发的核心机制,通过数据多版本和 ReadView 实现读写并行。理解它的原理,可以帮助我们更好地设计数据库事务,避免性能问题。