mvcc 简介

MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库中用于解决并发访问冲突的核心机制,广泛应用于InnoDB、PostgreSQL等主流数据库。其核心思想是为数据维护多个版本,使得读写操作可以并发执行而不互相阻塞,从而在保证事务隔离性的同时提升并发性能。

为什么需要MVCC?

传统的并发控制依赖锁机制(如行锁、表锁),但锁会导致读写冲突(例如:读操作需要等待写操作释放锁,或写操作等待读操作释放锁),在高并发场景下严重影响性能。

MVCC通过让读操作访问数据的历史版本,避免了对当前写入版本的依赖,从而实现"读不加锁、写不阻塞读",极大提升了并发效率。

MVCC的核心原理

MVCC的实现依赖三个关键组件:隐藏列 、undo日志 、Read View(读视图)。

1. 隐藏列:数据版本的"身份标识"

数据库表中的每行数据,除了用户定义的列外,还会隐含几个系统列(以InnoDB为例):

  • DB_TRX_ID:记录最后一次修改该数据行的事务ID(6字节)。
  • DB_ROLL_PTR:回滚指针(7字节),指向该数据行的上一个版本(存储在undo日志中)。
  • DB_ROW_ID:行唯一标识(6字节),当表没有主键时,InnoDB会用它生成聚簇索引。

这些隐藏列是MVCC追踪数据版本的基础。

2. undo日志:数据版本的"历史档案"

当事务修改数据时,数据库会先将数据的旧版本写入undo日志(回滚日志),然后再更新当前数据行。

  • 例如:事务T1修改了一行数据,旧版本会被存入undo日志,当前数据行的DB_ROLL_PTR指向这个旧版本;若事务T2再次修改该行,新的旧版本(T1修改后的版本)会被存入undo日志,DB_ROLL_PTR更新为指向T2的旧版本,形成一条版本链(通过回滚指针串联的历史版本)。
  • undo日志的另一个作用是事务回滚:若事务执行失败,可通过undo日志恢复数据到修改前的状态。
3. Read View:判断版本可见性的"规则"

Read View(读视图)是事务在读取数据时生成的一个"快照",用于判断当前事务能看到哪个版本的数据。它包含四个核心参数:

  • m_ids:当前活跃事务的ID集合(即尚未提交的事务)。
  • min_trx_id:m_ids中最小的事务ID(当前活跃事务的最小ID)。
  • max_trx_id:数据库下一个将要分配的事务ID(大于当前所有活跃事务ID)。
  • creator_trx_id:生成该Read View的事务自身的ID。
4. 可见性判断规则

当事务读取数据时,会通过Read View判断数据版本链中哪个版本对自己可见,规则如下(假设数据版本的DB_TRX_ID为trx_id):

  1. 若trx_id == creator_trx_id:该版本是当前事务自己修改的,可见。
  2. 若trx_id < min_trx_id:修改该版本的事务已提交(因为其ID小于所有活跃事务ID),可见。
  3. 若trx_id >= max_trx_id:修改该版本的事务是在当前事务之后启动的,不可见。
  4. 若min_trx_id <= trx_id < max_trx_id:
    • 若trx_id在m_ids中(该事务仍活跃):不可见。
    • 若trx_id不在m_ids中(该事务已提交):可见。

如果当前版本不可见,事务会通过DB_ROLL_PTR回溯到上一个版本,重复判断,直到找到可见版本或版本链结束(此时返回空)。

MVCC与事务隔离级别的关系

MVCC的行为会根据事务隔离级别调整,核心差异在于Read View的生成时机:

  • Read Committed(读已提交):每次执行查询时都会生成新的Read View。因此,同一事务中两次查询可能看到不同的结果(因为中间可能有其他事务提交)。
  • Repeatable Read(可重复读,InnoDB默认):仅在事务第一次执行查询时生成Read View,后续查询复用该视图。因此,同一事务中多次查询看到的结果一致(避免了不可重复读)。

(注:Serializable隔离级别通常不依赖MVCC,而是通过加锁实现;Read Uncommitted直接读取最新版本,不适用MVCC。)

MVCC的优势

  1. 读写不冲突:读操作无需加锁,直接访问历史版本;写操作仅锁定当前版本,不阻塞读,极大提升并发性能。
  2. 简化隔离级别实现:通过Read View的生成时机和可见性规则,自然实现了Read Committed和Repeatable Read隔离级别的语义。
  3. 避免锁竞争:减少了传统锁机制的阻塞和等待,降低了死锁风险。

总结

MVCC通过隐藏列记录版本标识 、undo日志维护历史版本链 、Read View判断可见性,实现了"多版本并发访问"。其核心价值是在保证事务隔离性的前提下,最大化读写并发效率,是现代数据库高性能的关键机制之一。

相关推荐
Decay__2 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
小徐不徐说6 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
论文复现现场7 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
Decay__8 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
心梦缘-雪雁9 小时前
SQL Server 查询数据存在哪个表里
数据库
lusklusklusk10 小时前
Python 巡检脚本:进程运行状态、数据库写入时效检查及邮件通知与告警
数据库·python·oracle
GeW10 小时前
产业新优势基础软件的人才底盘,从Linux与数据库开始
linux·数据库
SelectDB10 小时前
Doris Arrow Flight SQL 高速读取协议实战:与 Snowflake 客户端取数链路对照
大数据·数据库·数据分析
曼岛_10 小时前
[图解密码学]第4章 古典密码
java·数据库·密码学
瀚高PG实验室11 小时前
HGDB V9.0.5 .pgpass 免密登录提示 IsEncryptNon-base64 encoding 警告处理
数据库·postgresql·瀚高数据库