MySQL数据库中MVCC的底层原理

MySQL数据库中MVCC的底层原理 MVCC多版本并发控制是 MySQL 的 InnoDB 存储引擎实现高并发读写的核心技术。它的核心目标是实现**“读不加锁读写互不阻塞”**让读操作永远不阻塞写操作写操作也永远不阻塞读操作从而极大提升数据库的并发性能。我们可以把 MVCC 比作“图书馆借书”传统加锁机制你要借书读时管理员必须等你还书才能修改这本书的内容写两者互相阻塞。MVCC 机制你要借书时管理员直接复印一份当时的“副本”给你。与此同时管理员可以随意修改原书。你手里的副本不会受原书修改的影响。️ MVCC 的底层三大核心组件MVCC 的实现依赖于 InnoDB 的三大底层设计它们共同协作完成了多版本数据的管理行记录的隐藏字段InnoDB 的每一行数据除了我们自己定义的字段如id,name还会自动添加 3 个隐藏字段DB_TRX_ID事务ID记录最后一次插入或更新该行数据的事务 ID。DB_ROLL_PTR回滚指针指向该行数据在 Undo Log 中的上一个历史版本。DB_ROW_ID行ID如果没有设置主键InnoDB 自动生成的隐藏主键非 MVCC 核心仅作了解。Undo Log回滚日志与版本链当对某行数据进行 UPDATE 或 DELETE 操作时InnoDB不会直接覆盖原数据而是把旧版本的数据写入 Undo Log。更新当前行的DB_TRX_ID为当前事务 ID。更新当前行的DB_ROLL_PTR指向 Undo Log 中的旧版本。经过多次修改后这些历史版本通过DB_ROLL_PTR串联起来就形成了一条单向的“版本链”。Read View读视图Read View 是事务在进行“快照读”普通 SELECT时生成的一个“可见性判断规则”。它主要包含 4 个核心信息m_ids生成 Read View 时当前系统中所有活跃未提交的事务 ID 列表。min_trx_id活跃事务中最小的事务 ID。max_trx_id系统分配给下一个事务的 ID当前最大事务 ID 1。creator_trx_id当前事务自己的 ID。 版本可见性判断规则当一个事务去读取某行数据时它会拿着自己的 Read View从版本链的最新版本开始按照以下规则逐条判断该版本是否“可见”如果版本的DB_TRX_IDcreator_trx_id说明是当前事务自己修改的数据可见。如果版本的DB_TRX_IDmin_trx_id说明修改该行的事务在生成 Read View 之前就已经提交了可见。如果版本的DB_TRX_IDmax_trx_id说明修改该行的事务在当前事务之后才启动不可见。如果版本的DB_TRX_ID在min_trx_id和max_trx_id之间如果DB_TRX_ID在m_ids列表中说明该事务还未提交不可见。如果DB_TRX_ID不在m_ids列表中说明该事务已经提交可见。如果当前版本不可见就顺着DB_ROLL_PTR找上一个历史版本继续判断直到找到可见版本或遍历完整个版本链。 实战例子RC 与 RR 隔离级别的区别MVCC 在不同隔离级别下的表现核心区别在于Read View 的生成时机。场景设定有一张users表初始数据id1, nameAlice。例子 1读已提交RC级别规则每次执行 SELECT 语句时都会生成一个新的 Read View。时间事务 A事务 BMVCC 行为解析t0START TRANSACTION;t1SELECT name FROM users WHERE id1;生成 Read View 1读到Alice。t2START TRANSACTION; UPDATE users SET nameBob WHERE id1; COMMIT;B 修改并提交生成新版本 Bob旧版本 Alice 存入 Undo Log。t3SELECT name FROM users WHERE id1;生成新的 Read View 2根据规则能看到 B 提交的 Bob读到Bob。结果事务 A 在同一个事务内两次读到了不同的数据这就是**“不可重复读”**。例子 2可重复读RR级别MySQL 默认规则在事务第一次执行 SELECT 时生成 Read View整个事务期间复用这个 Read View。时间事务 A事务 BMVCC 行为解析t0START TRANSACTION;t1SELECT name FROM users WHERE id1;生成 Read View 1读到Alice。t2START TRANSACTION; UPDATE users SET nameBob WHERE id1; COMMIT;B 修改并提交生成新版本 Bob。t3SELECT name FROM users WHERE id1;复用 Read View 1。根据 Read View 1 的规则B 的事务 ID 属于“未提交活跃列表”因此 Bob 版本不可见顺着版本链读到旧版Alice。结果事务 A 两次读到的都是 Alice保证了**“可重复读”**。 补充说明快照读与当前读MVCC 仅对快照读Snapshot Read)生效也就是普通的SELECT语句。对于当前读Current Read例如SELECT ... FOR UPDATE、INSERT、UPDATE、DELETE数据库会跳过 MVCC直接读取数据的最新版本并且必须加锁排他锁或共享锁来保证数据修改的原子性和一致性。