MySQL 中的 MVCC
1. MySQL 中的 MVCC 是什么?
Multi-Version Concurrency Control (多版本并发控制)
一句话概括:
MVCC 是一种为了提高数据库并发性能的“魔法”。它通过在某个时间点为每个查询提供一个数据快照,使得读操作不会阻塞写操作,写操作也不会阻塞读操作。
核心思想:
不像传统的锁机制那样,大家必须排队等别人用完才能访问数据,MVCC 创建了数据的多个版本。当你来读数据时,数据库给你看的是符合你查询时刻条件的一个“历史版本”,而其他事务可以同时去修改最新的数据版本,互不干扰。
在 MySQL 的 InnoDB 存储引擎中,MVCC 是通过在每行记录后面保存多个版本来实现的。
2. Read View 在 MVCC 中如何工作?
Read View 可以理解为一个 “数据可见性快照” 或 “判断规则”。当事务发起一个一致性读(比如普通的 SELECT ...)时,InnoDB 会为这个事务生成一个 Read View,用它来决定当前事务能看到哪个版本的数据。
Read View 的核心组成部分:
m_ids: 生成 Read View 时,系统中活跃(尚未提交)的所有事务ID的列表。min_trx_id:m_ids中最小的事务ID。max_trx_id: 生成 Read View 时,系统应该分配给下一个事务的ID。creator_trx_id: 创建这个 Read View 的事务自己的ID。
判断规则(工作流程):
每行数据都有两个隐藏字段:
trx_id: 最近一次修改这行数据的事务ID。roll_pointer: 指向该行数据上一个版本的指针(存储在 undo log 中),形成一个版本链。
当一行数据被访问时,InnoDB 会顺着版本链,根据 Read View 的规则,从最新版本开始逐个判断:
-
如果
trx_id<min_trx_id:- 说明修改这行数据的事务在本次
Read View创建之前就已经提交了。 - 结论:这个版本对当前事务可见。
- 说明修改这行数据的事务在本次
-
如果
trx_id>=max_trx_id:- 说明修改这行数据的事务在本次
Read View创建之后才开启的。 - 结论:这个版本对当前事务不可见。 需要顺着
roll_pointer去找更旧的版本。
- 说明修改这行数据的事务在本次
-
如果
min_trx_id<=trx_id<max_trx_id:- 说明修改这行数据的事务在创建
Read View时可能活跃,也可能已提交。需要进一步判断: - 如果
trx_id在m_ids列表中,说明该事务当时还活跃着(未提交)。结论:这个版本对当前事务不可见。 - 如果
trx_id不在m_ids列表中,说明该事务在创建Read View时已经提交了。结论:这个版本对当前事务可见。
- 说明修改这行数据的事务在创建
-
如果
trx_id==creator_trx_id:- 说明这行数据是当前事务自己修改的。
- 结论:这个版本对当前事务可见。
简单总结: Read View 就像一个安检员,它只允许你看到那些在你“到场”(事务开始)之前就已经“坐稳”(事务提交)的数据,而不会让你看到那些后来才“进场”(事务开始)或还在“走动”(事务未提交)的数据。
3. 如果没有 MVCC 会怎样?
如果没有 MVCC,MySQL 要实现并发控制,将完全依赖于锁机制。这会带来一系列严重的问题:
-
严重的读写阻塞
- 读会阻塞写:当一个事务在执行一个长时间的查询时(例如
SELECT COUNT(*) FROM huge_table),它需要获取读锁。在这期间,任何其他事务都无法修改这张表里的任何数据,只能等待。 - 写会阻塞读:当一个事务在修改数据时,它会获取写锁。在这期间,其他所有事务都无法读取这些被锁住的数据。
- 读会阻塞写:当一个事务在执行一个长时间的查询时(例如
-
并发性能急剧下降
- 在高并发场景下(如电商秒杀、热门文章评论),大量的读写请求会相互阻塞,形成长长的等待队列。数据库的吞吐量(单位时间内处理的请求数)会变得非常低,响应时间变长,用户体验极差。
-
死锁几率增加
- 复杂的锁机制(如行锁、间隙锁等)在交织等待时更容易产生死锁。数据库需要花费额外的资源来检测和解除死锁,这本身也是一种性能损耗。
总结对比表:
| 特性 | 有 MVCC 的世界 | 没有 MVCC 的世界 |
|---|---|---|
| 读写并发 | 高。读写互不阻塞,可以同时进行。 | 低。读写相互阻塞,需要排队。 |
| 性能 | 高。尤其适合读多写少的应用。 | 低。高并发下性能瓶颈明显。 |
| 实现方式 | 通过数据多版本和 Read View 实现。 | 完全依赖于各种锁(共享锁、排他锁)。 |
| 用户体验 | 流畅,响应快。 | 卡顿,经常遇到“等待超时”。 |
最终总结
- MVCC 是 InnoDB 实现高并发的核心技术,它通过维护数据的多个版本来让读写操作不再相互等待。
- Read View 是 MVCC 的“裁判”,它定义了一套清晰的规则,来决定在某个特定时刻,一个事务能看到哪些数据版本。
- 没有 MVCC,读写冲突加剧、性能骤降、死锁频发。
