MySQL多版本控制-MVCC
1.什么是多版本并发控制(MVCC)
多版本并发控制(MVCC,Multiversion Concurrency Control)是一种数据库并发控制方法,它通过保留数据的多个版本来管理事务并发。与传统的锁机制不同,MVCC 允许多个事务同时读取和写入数据,而不会相互干扰,从而提高数据库的并发性和性能。
2 MVCC如何实现
2.1每行数据包含隐藏字段:
必选隐藏字段(始终存在):
DB_TRX_ID:记录最后一次修改该行数据的事务 ID。
DB_ROLL_PTR:指向 Undo Log 的指针,用于回溯历史版本数据。
条件性隐藏字段:
DB_ROW_ID:当表未显式定义主键且无唯一索引时,InnoDB 自动生成的隐藏主键;若存在自定义主键或唯一索引,则不会生成此字段
如上图所示,针对id=10001的这条数据,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为版本链,根据版本链就可以找到这条数据历史的版本。
2.2 Undo Log(回滚日志)
如2.1图所示,针对id=10001的这条数据,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,用于回滚和一致性读,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为版本链,根据版本链就可以找到这条数据历史的版本
利用undo log日志我们已经保留下了数据的各个版本,那么现在关键的问题是要读取哪个版本的数据呢? 这时就需要用到ReadView了,通过 Read View 判断数据版本的可见性
2.3 Read View(读视图)
ReadView就是事务在使用MVCC机制进行快照读操作时产生的一致性视图
事务启动时生成,包含以下信息:
●m_ids: 指的是在创建 ReadView 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表, “活跃事务”指的就是,启动了但还没提交的事务。
●min_trx_id: 指的是在创建 ReadView 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。
●max_trx_id:这个并不是 m_ids 的最大值,而是创建 ReadView 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;
●creator_trx_id :指的是创建该 ReadView 的事务的事务 id, 只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为 事务分配事务id,否则在一个只读事务中的事务id值都默认为0。
通过 Read View 判断数据版本的可见性,
√ DB_TRX_ID
= creator_trx_id 表示当前事务在访问它自己修改过的记录,可见
√ DB_TRX_ID
< min_trx_id 表示该版本在事务开始前已提交,可见
× DB_TRX_ID
> = max_trx_id 表示这个版本是由将来启动的事务生成的,不可见的
× DB_TRX_ID
属于 m_ids 表示这个版本是由还没提交的事务生成的,不可见,
√ DB_TRX_ID
不属于 m_ids 表示创建Read View时生成该版本的事务已经提交,可见
这种通过版本链 + 一致性视图 来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)。
3. MVCC 与隔离级别的关系
READ COMMITTED隔离级别下 : 每次 SELECT
生成新 Read View,总读取已提交的最新快照
REPEATABLE READ隔离级别下 : 事务首次 SELECT
生成 Read View,后续读操作复用该视图,保证可重复读
4.优点:
实现读写操作的非阻塞(读操作无需等待写操作释放锁)
避免传统锁机制带来的死锁风险和高并发下的性能瓶颈
5.缺点:
历史版本数据需存储于 Undo Log,增加存储开销。
频繁更新场景下,版本链过长可能影响查询效率。
6.优化建议
控制事务长度:避免长事务导致 Undo Log 堆积和版本链过长。
合理选择隔离级别:优先使用 REPEATABLE READ
平衡一致性与性能。
定期清理历史版本:通过配置 innodb_purge_threads
清理无效 Undo Log