mysql中的mvcc

综述

MVCC全称多版本并发控制。

主要原理就是对于同一份数据,在数据库中存储多份,每一份都是一个版本。然后根据事务发生的先后顺序,来决定哪些版本的数据对该事务可见,从而实现并发控制。

MVCC主要解决的问题

MVCC主要解决的是读写并发问题,可以在无锁情况下实现读写并发,从而可以提交读写效率。

关系型数据库中的读已提交、可重复读事务的隔离等级就是基于MVCC实现的。

MVCC在mysql中的应用

mysql中的Innodb存储引擎支持MVCC,对于Innodb表,mysql会默认为表创建几个隐藏列,其中比较重要的就是

  • tx_id ,事务id列,用于表明修改该行数据的事务id

  • 回滚指针列,由于Innodb支持事务,且默认事务的隔离等级为可重复读,其内部对于一行数据,会存储多个版本,回滚指针用于指向在事务修改该行数据之前,该行数据的样子(即该行数据的上一个版本),如下图所示,图片来自 MySQL的多版本并发控制(MVCC)是什么

    Undo Log回滚历史记录

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_idread view集合中,表示当前事务开启时,tx_id事务还未提交,因此这行数据的这个版本对当前事务不可见
  • low_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