MVCC实现原理及其作用

这里写自定义目录标题

为什么要有MVCC?

从用户需求出发,数据库想要实现什么功能呢?事务想要实现隔离性,也就是如果多个事务同时运行,各个事务之间不能相互打扰(不能有脏读、不可重复读、幻读)。其实加锁可以简单粗暴的解决这个问题,但如果连读操作都频繁加锁,显然会严重影响效率,因此如何在不加锁的情况下解决多个事务的并发读问题,是多版本并发控制(MVCC)实现的出发点。

如何实现?

现在我们有很多个事务,它们面对着同一张表,表里有很多条数据。有的事务可能在修改某条记录,有的在查询,有的在删除。如何让每个事务只能看到自己应该看到的数据呢?

首先我们想到,应该给每个事务编号,并且这个编号和每一行数据有一个对应关系,这样才能控制某个事务只能看某些数据。因此,INNODB在每行记录中存放了一个隐藏字段trx_id即最近一次插入或更新这条记录的事务id。

然后,每个事务应该知道自己当前能看哪些事务的数据,这里就要区分事务的隔离级别了,读已提交 显然要比可重复读能看到的数据多一些。Mysql 通过Read View来记录创建一个事务时,系统中其他事务的相关信息,并以此来判断当前事务对某条数据的可见性。

Read View以及可重复读的隔离原理

顾名思义,Read View 就是个"读视图",或者说事务在创建时"拍了个快照"。如果类比java的数据结构的话,可以把它看做事务中的一部分,一个内部类。

java 复制代码
class ReadView {
	trx_id[] m_ids; //表示在生成readview时,当前系统中活跃的读写事务id数组; 
	trx_id min_trx_id; /*表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,
	 					 也就是m_ids中最小的值,低水位; */
	trx_id max_trx_id; //表示生成readview时,系统中应该分配给下一个事务的id值,高水位; 
	trx_id creator_trx_id; //表示生成该readview的事务的事务id;
}

以上代码并不是真实的实现哈,只是为了方便理解,临时编写的一个类。

我们先以可重复读隔离级别为例,在Repeatable Read隔离级别下,Mysql将在启动事务时创建一个Read View,因此事务后续在访问数据记录时会有以下几种情况:

  • 如果记录的 trx_id 值等于Read View中的creator_trx_id,也就是说这条记录就是当前事务创建的,显然该记录可见;
  • 如果记录的 trx_id 值小于Read View中的min_trx_id,表示该版本的记录在本事务创建之前就有了,因此可见;
  • 如果记录的 trx_id 值大于Read View中的max_trx_id,表示该版本的记录在本事务创建之后才创建,因此不可见;
  • 如果记录的 trx_id 值介于min_trx_id和max_trx_id之间,就要进一步判断trx_id是否在数组m_ids中:
    • 如果trx_id在数组m_ids中,表明生成该记录的事务还没提交,处于活跃状态,因此该记录对当前事务不可见;
    • 如果trx_id不在数组m_ids中,表明生成该记录的事务已经被提交,因此该记录可见。

这时还有一个问题,就是某条数据因为事务状态的原因导致不可见,但是这条数据之前就存在了,也就是说当前事务应该看到这条数据之前的版本,这应该如何实现呢?

其实,在mysql的数据行中还有一个隐藏列,就是roll_pointer,每次事务对记录创建或修改时,都会将旧版本的数据计入undo日志,roll_pointer则负责以指针的形式连接这些不同版本的数据。当一个版本的trx_id不可见时,会顺着roll_pointer向以前的版本寻找,直到数据可见或没有数据。

读已提交是如何实现的?

上面描述了可重复读的工作原理,那读已提交是如何实现的呢?其实它俩的区别就一句话:

可重复读只在事务开始时创建一次Read View,读已提交会在每次读操作时创建Read View。

这就意味着,在可重复读隔离级别下,如果事务A在执行过程中,其他事务提交了数据,A相当于是完全不知道的,它将会读到事务开始时的数据;而在读已提交隔离级别下,每次读操作都会刷新Read View,里面的各项参数也随之被更新,其他事务提交的修改就可见了。

总结

本文主要介绍了MVCC的工作原理,以及数据中的隐藏列和Read View是如何配合的。需要注意的是,MVCC只解决了快照读的隔离问题(也就是普通的select语句),这对于读已提交隔离级别来说已经足够,因为当前读和快照读的表现是一样的。而对于可重复读隔离级别来说,当前读还会产生幻读和不可重复读现象,这需要加锁解决,该问题将在后续文章中阐述。

相关推荐
Mr_Xuhhh几秒前
C语言字符串与内存操作函数模拟实现详解
java·linux·算法
瑞雪兆丰年兮1 分钟前
[从0开始学Java|第十一天]ArrayList
java·开发语言
B站_计算机毕业设计之家3 分钟前
AI大模型:Deepseek美食推荐系统 机器学习 协同过滤推荐算法+可视化 Django框架 大数据毕业设计(源码)✅
python·算法·机器学习·数据分析·django·推荐算法·美食
TDengine (老段)3 分钟前
TDengine TSDB 3.4.0.0 上线:虚拟表、流计算性能显著提升,安全能力全面进阶
大数据·数据库·物联网·安全·时序数据库·tdengine·涛思数据
Leo.yuan4 分钟前
制造业常用BOM详解:单层BOM、多层BOM、工艺BOM、虚拟BOM
大数据·数据库·信息可视化·bom
小草cys4 分钟前
基于大模型的图像目标检测及跟踪算法
人工智能·算法·目标检测
夜郎king5 分钟前
基于 Java 实现数九天精准计算:从节气算法到工程化落地
java·开发语言
新缸中之脑7 分钟前
Nanobot:轻量级OpenClaw
java·运维·网络
悟能不能悟11 分钟前
java.sql.SQLSyntaxErrorException: ORA-01031: insufficient privileges
java·开发语言
马猴烧酒.12 分钟前
【DDD重构|第十三天】DDD 领域驱动设计详解+实战
java·jvm·ide·重构·tomcat·maven·团队开发