深入分析理解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. 优化查询,利用覆盖索引减少回表。
相关推荐
愿你天黑有灯下雨有伞15 分钟前
枚举策略模式实战:优雅消除支付场景的if-else
java·开发语言·策略模式
菜鸟学Python31 分钟前
Python web框架王者 Django 5.0发布:20周年了!
前端·数据库·python·django·sqlite
Dcs42 分钟前
gRPC性能陷阱:低延迟网络下的客户端瓶颈揭秘
java
小云数据库服务专线43 分钟前
GaussDB 查看会话连接数
数据库·gaussdb
探索java1 小时前
深入解析 Spring 获取 XML 验证模式的过程
xml·java·spring
找不到、了1 小时前
Java设计模式之<装饰器模式>
java·设计模式·装饰器模式
java叶新东老师1 小时前
解决windows系统下 idea、CLion 控制台中文乱码问题
java·windows·intellij-idea
岁忧2 小时前
(LeetCode 面试经典 150 题 ) 155. 最小栈 (栈)
java·c++·算法·leetcode·面试·go
yourkin6662 小时前
为什么现在 Spring Boot 默认使用 CGLIB 了?
java·开发语言·jvm
墨迹的陌离2 小时前
【Linux】重生之从零开始学习运维之Mysql
linux·运维·服务器·数据库·学习·mysql