深入分析理解mysql的MVCC

一、MVCC 的核心思想

目标

允许读写操作并发执行,读操作不阻塞写操作,写操作不阻塞读操作,从而避免锁竞争。

基本原理

  • 每个事务修改数据时生成数据的新版本(版本链)。
  • 读操作根据事务启动时刻的"快照"读取对应版本的数据,而非直接读取最新数据。

二、MVCC 的底层实现

1. 数据行的隐藏字段

InnoDB 每行数据包含三个隐藏字段:

字段名 描述
DB_TRX_ID 最近修改该行的事务ID(插入或更新时更新)
DB_ROLL_PTR 回滚指针,指向 Undo Log 中旧版本数据的指针(构成版本链)
DB_ROW_ID 隐式自增行ID(无主键时自动生成)

2. Undo Log 与版本链

  • Undo Log:记录数据修改前的旧值,用于回滚和构建历史版本。

  • 版本链 :通过 DB_ROLL_PTR 将同一行数据的多个版本连接成链表,结构如下:

    plaintext 复制代码
    最新数据行 → Undo Log V3 → Undo Log V2 → Undo Log V1

    每个 Undo Log 包含:旧数据值 + 生成该版本的事务ID(DB_TRX_ID)。

3. ReadView(读视图)

  • 作用:决定事务能看到哪些版本的数据。
  • 关键属性
    • creator_trx_id:创建该 ReadView 的事务ID。
    • m_ids:生成 ReadView 时活跃的事务ID集合。
    • min_trx_idm_ids 中的最小事务ID。
    • max_trx_id:生成 ReadView 时系统将分配的下一个事务ID。

三、MVCC 的可见性规则

判断数据版本对当前事务是否可见的算法:

  1. 数据版本的 DB_TRX_ID < min_trx_id → 可见(版本在 ReadView 生成前已提交)。
  2. 数据版本的 DB_TRX_ID > max_trx_id → 不可见(版本在 ReadView 生成后创建)。
  3. 数据版本的 DB_TRX_ID ∈ [min_trx_id, max_trx_id)
    • DB_TRX_ID ∉ m_ids → 可见(事务已提交)。
    • DB_TRX_ID ∈ m_ids → 不可见(事务未提交)。
  4. 若数据版本由当前事务自身修改 → 可见。

遍历版本链:从最新版本开始,依次判断直到找到第一个可见的版本。


四、MVCC 在事务隔离级别中的行为

1. READ COMMITTED(读已提交)

  • ReadView 生成时机:每次执行 SELECT 时生成新 ReadView。
  • 效果
    能读取到其他事务已提交的最新数据,存在不可重复读和幻读问题。

2. REPEATABLE READ(可重复读)

  • ReadView 生成时机:第一次执行 SELECT 时生成,后续复用同一 ReadView。
  • 效果
    整个事务中看到的数据版本一致,解决了不可重复读,但幻读需配合间隙锁。

五、MVCC 的读写操作流程

1. SELECT 操作(快照读)

plaintext 复制代码
1. 获取事务的 ReadView
2. 遍历数据行的版本链
3. 返回第一个符合可见性规则的版本数据

2. UPDATE/DELETE 操作(当前读)

plaintext 复制代码
1. 加行锁(X锁)
2. 读取最新数据版本(需处理其他事务的锁冲突)
3. 生成新版本数据并插入版本链
4. 更新 DB_TRX_ID 和 DB_ROLL_PTR

3. INSERT 操作

plaintext 复制代码
1. 直接插入新行,DB_TRX_ID = 当前事务ID
2. DB_ROLL_PTR 指向空(无历史版本)

六、MVCC 的优缺点

优点

  • 高并发:读写操作不互相阻塞。
  • 避免锁竞争:减少死锁概率。
  • 快速回滚:利用 Undo Log 实现事务原子性。

缺点

  • 存储开销:需维护多版本数据和 Undo Log。
  • 历史版本清理:长事务可能导致 Undo Log 无法及时清理(Purge 线程压力)。
  • 写冲突检测延迟:需通过锁机制补充(如间隙锁)。

七、实战案例:事务并发场景

场景描述:

  • 事务A(TrxID=100):SELECT * FROM users WHERE id=1;
  • 事务B(TrxID=101):UPDATE users SET name='Bob' WHERE id=1; COMMIT;
  • 事务A再次执行相同 SELECT。

执行过程(隔离级别=REPEATABLE READ):

  1. 事务A首次 SELECT

    • 生成 ReadView:creator_trx_id=100, m_ids=[100,101], min=100, max=102
    • 若数据行 DB_TRX_ID=99(已提交)→ 可见。
  2. 事务B提交 UPDATE

    • 数据行更新为 DB_TRX_ID=101,生成新版本。
  3. 事务A再次 SELECT

    • 复用原 ReadView。
    • 最新版本 DB_TRX_ID=101(属于 m_ids 且未提交 → 不可见)。
    • 继续查找旧版本 DB_TRX_ID=99 → 可见。

结果:事务A两次读取结果一致(可重复读)。


八、总结

MVCC 是 MySQL 高并发能力的基石,通过 版本链 + ReadView + Undo Log 的组合实现非锁定读。理解其机制可帮助我们:

  1. 合理设计事务边界,避免长事务导致版本堆积。
  2. 优化查询,利用覆盖索引减少回表。
相关推荐
不辉放弃17 分钟前
java连数据库
java·mysql
GzlAndy21 分钟前
MySQL全局优化
数据库·mysql
m0_7415747524 分钟前
mysql主从同步
数据库·mysql
小白教程1 小时前
MySQL数据库的安全性防护
数据库·mysql
Lion Long1 小时前
CodeBuddy 中国版 Cursor 实战:Redis+MySQL双引擎驱动〈王者荣耀〉战区排行榜
数据库·redis·mysql·缓存·腾讯云·codebuddy首席试玩官·codebuddy
apcipot_rain4 小时前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
辛一一7 小时前
neo4j图数据库基本概念和向量使用
数据库·neo4j
熊大如如8 小时前
Java 反射
java·开发语言
巨龙之路8 小时前
什么是时序数据库?
数据库·时序数据库
蔡蓝8 小时前
binlog日志以及MySQL的数据同步
数据库·mysql