MVCC是什么:

**
MVCC的全称是Muti-Version Concurrency Control,中文是多版本并发控制.它是一种并发控制的策略,高效的实现对数据库的并发访问.
MySQL的大多数事务型存储引擎实现的都不是简单的行级锁.基于提升并发性能的考虑,它们一般都同时实现了MVCC,包括Oracle等数据库系统也实现了MVCC.
MySQL的InnoDB引擎 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。

RR和RC生成ReadView的策略不同:RC在每次查询的开始都会生成一个独立的ReadView,而RR则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

**

本文主要解释InnoDB引擎的RR隔离级别下MVCC的实现原理

**

transactin id;

每个事务开始的时候都会向InnoDB的事务系统申请唯一的按申请顺序严格递增的事务ID,叫作transactin id;

row trx_id 和 数据的版本链

数据库中每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说新老数据可以通过版本链的方式记录,新数据可以通过版本里从undo log中拿到老数据

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_mysql


一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上一个版本”。

实现

事务在启动的时候会生成一个包含了当前所有活跃事务(已经开始但是还未提交的事务的数组)

数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)

row_trx_id 和 read_view 的对比来实现事务是否可见

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_数据_02


当我们读一行数据的时候,该数据的row_trx_id可能有三种情况…

1:row_trx_id<readview 的低水位,表示row_trx_id对应的事务id已经提交.可以看到

2:row_trx_id>readview的高水位 ,表示row_trx_id对应的事务id未开始,不能看到该行数据,通过rollpointer

和undolog找上一个版本的数据,继续比较,直到找到能看到的数据…

3:row_trx_id>= 低水位 并且 row_trx_id <= 高水位

如果row_trx_id 在活跃数组内,表示已经开始未提交的事务,不可见.回滚数据链版本,继续比较.

如果row_trx_id 不在活跃数组内,表示已经提交的事务,可见例一:

user表 id 1 age 1

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_数组_03


readview [10,20] 21 当前数据的row_trx_id 为20,在活跃数组内,不可见,只能去找上一个版本的数据;

age = 10

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_回滚_04

readview [10],20 当前数据的row_trx_id 为10,落在了b区,在活跃数组内,不可见,只能去找上一个版本的数据;上一个版本的row_trx_id = 20,b区,活跃数组外,可见… age = 20

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_数据_05

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_数据_06

一个查询sql:read view [10] row_trx_id 10 11,在活跃数组内,不可见 回滚数据链版本 最后结果 age = 1

mysql mvcc解决什么问题 mysql的mvcc实现原理详解_数组_07


RR第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

所以readview 还是[10] 11,row_trx_id (20)> readview 的高水位,表示事务为开启,不可见,回滚版本链后row_trx_id (10),落在b区,但仍然在活跃数组内,不可见,继续回滚版本链 最后结果 age = 1;

RR级别下,同一个事务内两次查询age =1.所以是可重复查询的.

如果是RC,每次查询都生成一个新的readview.
第二次查询,readview [10]20,21 ,row_trx_id(20) 在readview的活跃数组外,表示已提交事务,可以看到,结果为age = 20;
RC级别下,同一个事务内两次查询age不同,造成不可重复读

如果是delete 语句,会在该行数据中加一个flag,回滚版本链的时候,通过该flag发现是delete后的数据,就会跳过,直接再找下一个版本链的数据