MySQL MVCC 多版本并发控制

144 阅读3分钟

引言

在多事务并发执行中,存在脏读、幻读、不可重复读的问题。所以在数据库的发展之初,在读取以及写入数据的时候,使用了锁来保证数据读取以及写入的安全。但是加锁会带来性能问题。所以大量的工程师以及科研人员对此进行优化,针对读取和写入分别进行优化。

对于读取写操作的优化,目前使用比较多的优化思路就是 MVCC ,可以提升数据库对读取以及写入的性能。

换句话讲: 在事务并发安全执行有两种解决思路,一种是加锁,一种时使用版本控制(MVCC)的办法。单纯的只是加锁使得性能会比较低下,但是使用了版本控制之后,既可以保证运行的效率,又可以保证并发事务的安全。

不同的 DBMS ,不同的存储引擎对于 MVCC 的的实现是有所不同的,下面主要讲述的是 InnoDB 的 MVCC 的实现。更准确来说是 RR 以及 RC 两种隔离级别下面的 MVCC 的实现(因为InnnoDB只有这两种隔离级别使用了 MVCC 的思想,并且进行了具体的实现)

什么是 MVCC (狭义:InnnoDB 中 RR RC 两种隔离级别实现的底层思想)

对数据库任何修改的提交不会直接覆盖之前的数据,而是产生一个老版本和新版本的数据共存,使得在读取数据是完全不加锁。在这种情况下读取数据时,使得事务可以根据隔离级别选择读取哪儿个版本的数据。实现了多事务并发执行的安全性。

在 InnoDB 多事务并发操作中,可以设置隔离等级,选择合适的隔离等级可以解决多事务并发操作中的脏读、幻读、不可重重复读的问题。

而 InnoDB 中的隔离级别 RC(Read Committed) 和 RR(Repeatable Read) 的实现就是基于 MVCC 思想的。

RC RR 隔离级别下的 MVCC 的作用

参考知乎回答:


1.  一个事务A(txnId=100)修改了数据X,使得X=1,并且commit了
1.  另外一个事务B(txnId=101)开始尝试读取X,但是还X=1。但B没有提交。
1.  第三个事务C(txnId=102)修改了数据X,使得X=2。并且提交了
1.  事务B又一次读取了X。这时

-   如果事务B是Read Committed。那么就读取X的最新commit的版本,也就是X=2
-   如果事务B是Repeatable Read。那么读取的就是当前事务(txnId=101)之前X的最新版本,也就是X被txnId=100提交的版本,即X=1。

注意,这里B不论是Read Committed,还是Repeatable Read,都不会被锁,都能立刻拿到结果。这也就是MVCC存在的意义。

MVCC 解决了什么问题?

简而言之就是解决了在REPEATABLE READ和READ COMMITTED两个隔离级别下读取某一行数据时多事务的并发安全。

知乎回答: MVCC解决了什么问题?

MVCC 具体的实现原理

参考

www.zhihu.com/question/27… juejin.cn/post/701616…

image.png

image.png

image.png

image.png

image.png

MVCC 使用是存在条件的,只有在快照读才会使用,在 InnoDB 的 RR RC 隔离级别下才会使用 MVCC

当前读使用的是行锁以及间隙锁实现

快照读以及当前读在 RR RC 下面执行是完全不同的 image.png

image.png

image.png

image.png

下面是 RC 级别下面的 ReadView 以及数据版本的获取: image.png 下面是 RC 级别下面的 ReadView 以及数据版本的获取: image.png

下面是 RR 级别下面的 ReadView 的 MVCC: image.png 下面是 RR 级别下面的 ReadView 的 MVCC: image.png

image.png

image.png 下面是两次快照读中间夹着一次当前读,导致了幻读问题:

image.png