深入分析理解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. 优化查询,利用覆盖索引减少回表。
相关推荐
张紫娃2 分钟前
Spring @Scope, @Lazy, @DependsOn, @Required, @Lookup
java·后端·spring
简诚3 分钟前
java实现RabbitMQ消息发送和接收功能(包含测试)
java·rabbitmq·java-rabbitmq
檀越剑指大厂1 小时前
【PostgreSQL系列】PostgreSQL WAL 目录配置
数据库·postgresql
結城1 小时前
Spring Security如何拿到登录用户的信息
java·spring·mybatis
GalaxyPokemon2 小时前
LeetCode - 2. 两数相加
java·前端·javascript·算法·leetcode·职场和发展
运维行者_3 小时前
Azure数据库监控:如何在2025年选择合适的工具
运维·服务器·网络·数据库·flask·自动化·azure
dualven_in_csdn5 小时前
搞了两天的win7批处理脚本问题
java·linux·前端
C++chaofan7 小时前
74. 搜索二维矩阵
java·算法·leetcode·矩阵
诺浅8 小时前
AWS S3 SDK FOR JAVA 基本使用及如何兼容七牛云
java·spring boot·aws