MySQL里MVCC原理

MVCC的核心就是快照,例如在可重复读隔离级别下,启动一个事务,就相当于创建了一个全库的快照。

那么MVCC是如何创建快照的,如果数据库大小是100G,难道要花费100G的空间做快照?并且这还只是一个一个session的快照,如果同时100个session请求过来,难道要花费100*100G的空间?
很显然MVCC肯定不是这样实现的。

InnoDB 里面每个事务有一个唯一的事务ID,是按照事务启动的先后顺序,向系统申请的。
每一行数据同一时刻可以存在多个事务ID,也就是一行数据在同一时刻存在多个数据版本,各个事务各自数据版本和各自的数据ID相关联。

这样说比较抽象,看下面的版本流程图:

---------------------------------------------------
	session1	session2	session3	
初始版本	 set a=2	set a=3		set a=3
---------------↓-----------↓-----------↓-----------			
V1 a=1	    ←V2 a=2	←V3 a=3	      ←V4 a=4
trx_id=100  trx_id=101	trx_id=103    trx_id=104
---------------------------------------------------
V1←V2←V3←V4
---------------------------------------------------

既然一行数据有多个版本,那么各个事务读取数据,看到的数据应该是哪个版本呢?
数据是否可见,依赖一套规则来判断。当一个事务启动时,InnoDB为每个事务创建了一个事务数组,也就是当前活跃的事务,放到一起,创建了一个数组,当前事务可见的数据,都是依赖这个数组来进行判断的。
这个数组分为三部分:已提交事务、未提交事务、未开始事务。

数组效果如下图:

---------------------------------------------------	
	      当前事务
	低水位	↓  高水位
	  ↓	↓  ↓
已提交事务||未提交事务||未开始事务
---------------------------------------------------

我们按照可重复读的隔离级别来分析,有三种情况:
1、版本未提交,不可见;
2、版本已提交,但是在视图创建后提交的,不可见;
3、版本已提交,且是在视图创建前提交的,可见。

还有一个非常重要的点,就是更新逻辑,因为按照读的可见性判断,A事务如果在B事务之后启动,并更新了一条数据,B事务是不可见的,此时B事务也要更新这条数据,如果不可见,那么就会出现A事务更新的结果丢失了,这显然是不正确的。
那么更新则是按照先读后写的的逻辑来处理,也就是所有的提交,都是可见的,并基于这个值,进行更新。