innodb如何实现mvcc的

InnoDB 实现 MVCC(多版本并发控制)的机制主要依赖于 Undo Log(回滚日志)Read View(读视图)隐藏的事务字段。以下是具体实现步骤和原理:


1. 核心数据结构

InnoDB 的每一行数据(聚簇索引记录)包含两个隐藏字段:

  • DB_TRX_ID:最近修改该行的事务 ID。
  • DB_ROLL_PTR:指向该行旧版本数据的回滚指针(即 Undo Log 的地址)。

2. Undo Log 与版本链

  • Undo Log 的作用
    每次对数据进行修改(INSERT/UPDATE/DELETE),InnoDB 会生成 Undo Log,记录修改前的数据镜像。

  • 版本链的构建
    通过 DB_ROLL_PTR 字段,将同一行数据的多个版本按修改顺序链接成链表。
    示例

    plaintext 复制代码
    当前行 → [版本3: trx_id=300, roll_ptr → 版本2]  
                ↑  
    版本2 → [trx_id=200, roll_ptr → 版本1]  
                ↑  
    版本1 → [trx_id=100, roll_ptr → NULL]

3. Read View 的生成

当事务执行 一致性读 (如 SELECT)时,InnoDB 为其生成一个 Read View,包含以下信息:

  • m_ids:当前活跃(未提交)的事务 ID 列表。
  • min_trx_idm_ids 中的最小事务 ID。
  • max_trx_id:下一个即将分配的事务 ID(即当前最大事务 ID +1)。
  • creator_trx_id:创建该 Read View 的事务 ID(仅当该事务自身有修改时存在)。

4. 数据可见性判断规则

对于某一数据行的版本,判断其对当前事务是否可见的规则如下:

  1. 版本的事务 ID < min_trx_id
    该版本已提交,可见。
  2. 版本的事务 ID ≥ max_trx_id
    该版本由未来事务生成,不可见。
  3. min_trx_id ≤ 版本的事务 ID < max_trx_id
    • 若版本的事务 ID 不在 m_ids,说明该事务已提交,可见。
    • 若版本的事务 ID m_ids,说明该事务未提交,不可见。
  4. 版本的事务 ID = creator_trx_id
    该版本由当前事务自身修改,可见。

5. MVCC 的查询流程

  1. 定位最新数据行:通过聚簇索引找到当前行的最新版本。
  2. 遍历版本链 :从最新版本开始,根据 DB_ROLL_PTR 回溯旧版本。
  3. 可见性检查:对每个版本应用 Read View 规则,找到第一个可见的版本。

示例

假设事务 A(trx_id=200)的 Read View 中:

  • m_ids = [100, 300]
  • min_trx_id=100, max_trx_id=400

遍历某行的版本链:

  • 版本3(trx_id=300) :在 m_ids 中 → 不可见。
  • 版本2(trx_id=200) :等于 creator_trx_id → 可见(若事务 A 修改了该行)。
  • 版本1(trx_id=100) :在 m_ids 中 → 不可见。
  • 版本0(trx_id=50) :小于 min_trx_id → 可见。

最终事务 A 读取到版本0。


6. 不同隔离级别的实现差异

  • 读已提交(Read Committed)
    每次执行 SELECT 时生成新的 Read View,看到已提交的最新数据。
  • 可重复读(Repeatable Read)
    事务内第一次 SELECT 时生成 Read View,后续复用该视图,保证多次读取一致性。

7. Undo Log 的清理

  • Insert Undo Log:事务提交后立即删除(不参与 MVCC)。
  • Update/Delete Undo Log :需保留至所有可能访问旧版本的事务结束(通过 purge 线程异步清理)。

8. MVCC 与锁的协同

  • 写操作(UPDATE/DELETE)
    即使使用 MVCC,写操作仍需加锁(如行锁)避免脏写。
  • 读操作(SELECT)
    默认无锁,通过 MVCC 读取快照版本。若需锁定读,可加锁(如 SELECT ... FOR UPDATE)。

总结

InnoDB 通过以下机制实现 MVCC:

  1. 版本链 :通过 DB_ROLL_PTR 链接数据的历史版本。
  2. Read View:判断事务可见性的依据。
  3. Undo Log:存储历史版本数据,支持版本链回溯。
  4. 隔离级别适配:通过动态生成或复用 Read View 实现不同隔离级别。
plaintext 复制代码
# MVCC 执行示例
事务A(trx_id=200)读取某行数据:
1. 找到最新版本(trx_id=300)。
2. 检查 Read View:m_ids=[100, 300], min=100, max=400。
3. trx_id=300 在 m_ids 中 → 不可见。
4. 回溯到版本2(trx_id=200),等于 creator_trx_id → 可见。
相关推荐
嘉然今天吃粑粑柑31 分钟前
🚀 时序数据库:机器监控数据采集的最佳解决方案
数据库
LaughingZhu1 小时前
PH热榜 | 2025-04-03
前端·数据库·人工智能·经验分享·mysql·开源·产品运营
GalaxyPokemon1 小时前
MySQL基础 [三] - 数据类型
数据库·mysql
爱的叹息1 小时前
数据库分库分表中间件及对比
数据库·中间件
ConardLi2 小时前
MCP + 数据库,一种比 RAG 检索效果更好的新方式!
javascript·数据库·人工智能
纪元A梦2 小时前
Redis最佳实践——用户会话管理详解
数据库·redis·缓存
多云的夏天2 小时前
麒麟-QT-ODBC-达梦
linux·数据库·麒麟
京东云开发者2 小时前
ClickHouse 的“独孤九剑”:极速查询的终极秘籍
数据库
码熔burning3 小时前
Redis 线程模型:单线程也能快如闪电?
数据库·redis·缓存
不断前进的皮卡丘3 小时前
06-公寓租赁项目-后台管理-公寓管理篇
java·开发语言·数据库·spring boot