综述
MVCC全称多版本并发控制。
主要原理就是对于同一份数据,在数据库中存储多份,每一份都是一个版本。然后根据事务发生的先后顺序,来决定哪些版本的数据对该事务可见,从而实现并发控制。
MVCC主要解决的问题
MVCC主要解决的是读写并发问题,可以在无锁情况下实现读写并发,从而可以提交读写效率。
关系型数据库中的读已提交、可重复读事务的隔离等级就是基于MVCC实现的。
MVCC在mysql中的应用
mysql中的Innodb存储引擎支持MVCC,对于Innodb表,mysql会默认为表创建几个隐藏列,其中比较重要的就是
tx_id ,事务id列,用于表明修改该行数据的事务id
回滚指针列,由于Innodb支持事务,且默认事务的隔离等级为可重复读,其内部对于一行数据,会存储多个版本,回滚指针用于指向在事务修改该行数据之前,该行数据的样子(即该行数据的上一个版本),如下图所示,图片来自 MySQL的多版本并发控制(MVCC)是什么
mvcc实现可重复读
mysql会按照事务的先后顺序,以严格递增方式为每一个事务分配一个唯一的事务id。
当mysql开启一个事务时,会立即形成一个快照,称之为read view
,read view
中记录了所有当前活跃的事务id(所谓活跃,是指事务已经开始,但是还没有结束)。在read view
中的活跃事务集合中,存在一个最小id的事务,假设称之为low_tx_id
, 存在一个最大事务id,high_tx_id
。当前事务称之为cur_tx_id
,假定cur_tx_id
要读取的那行数据的隐藏事务列为tx_id
,则可能存在如下几种情况。
这里需要说明一点,当前事务id不一定是
read view
中的最大值,因为在开启事务时,生成read view
之前,可能会有新的事务进来,这个时候就会出现read view
中的rv_max_tx_id
比当前事务id大的情况
cur_tx_id = tx_id
,表示这行数据就是由当前事务修改的,必然对该事务可见tx_id < low_tx_id
,表示在当前事务开启的时候,tx_id
所代表的事务已经提交了,因此这行的该版本数据,对当前事务可见low_tx_id <= tx_id <= high_tx_id
, 且tx_id
在read view
集合中,表示当前事务开启时,tx_id
事务还未提交,因此这行数据的这个版本对当前事务不可见- l
ow_tx_id <= tx_id <= high_tx_id
, 且tx_id
不在read view
集合中,表示当前事务开启时,tx_id
事务已经提交,因此这行数据对当前事务可见 tx_id > high_tx_id
,表示tx_id
事务是在当前事务开启之后提交的,因此数据对当前事务不可见
mvcc通过如上原理,再加上回滚指针,按照如下流程就行读取。
当读取的数据版本,对当前事务可见时,则可以直接读取,结束
当读取的数据版本,对当前事务不可见时,则按照改行的隐藏列回滚指针列,去读取这上数据的上一个版本(这个数据是存储在undo日志中的)
首先读取这个版本对应的事务id列,获取
tx_id
,并与上述规则进行匹配,成功,则该版本数据对当前事务可见,结束; 否则继续第二步
MVCC实现读已提交
在读提提交隔离级别,MVCC遵循的规则和上面一致,不同的点是,在事务执行过程中,每一次的读操作,都会创建一个read view
。
这样的话,read view
不断更新,因此之前不可见的数据,随着对应事务的提交,也会对当前事务可见,从而就实现了读已提交。具体流程如下。
- 读事务开启
- 当要进行读取操作时,立即生成一个
read view
,并获取对应行数据,安装MVCC规则进行匹配,匹配成功,则该版本数据对当前事务可见,否则根据回滚指针读取上一个版本的数据,直至读取到对当前事务可见的版本 - 其他操作
- 当再次涉及到读操作时,继续进行第二步操作
写写控制
MVCC实现了读写之前的无锁并发控制,而对于写写操作,Innodb表采用锁机制来进行并发控制,不过它采用的是行锁来进行控制,与表锁相比,它的粒度更细,锁定的范围更小,因此效率更高。
参考:MySQL的多版本并发控制(MVCC)是什么? https://segmentfault.com/a/1190000037557620