MySQL -MVCC
在innoDB引擎中 有隐式字段,来标记每一行数据的版本(riwtrx_id),同时还有一个字段指向上一个版本的内存地址值。
图上是一行数据的4个版本。 V4版本 值是22,transaction id 为25的事务更新,rowtrx_id 是25
v1-v3 在物理上并不存在,而是每次需要的时候根据当前版本,和
undo log
计算出来。
InnoDB为每一个事务构造了一个数组(数组最ID值为低水位,系统里已经创建的事务ID最大值+1 为高水位),用来保存这个事务启动的瞬间,数据的可见性规则,就是基于数据的rowtrx_id和这个一致性性视图(read-view)的对比结果得到的
- 以提交事务: 这个版本是已经提交的事务或者是当前事务自己生成的,这个数据是可见的
- 未提交事务集合:
- rowtrx_id ; 在数组中,这个版本是还没提交的事务生成的,不可见。
- rowtrx_id:不再数组中,这个版本是已经提交了的事务生成的,可见。
- 未开始事务: 这个版本是由将来启动的事务生成的,是肯定不可见的。
例如:上图中 如果低水位为 18 ,那么它访问这一行数据时,就从V4 通过 U3 计算出 V3 读取的值时11。
历史 90 事务版本 (id =1 , value= 1)
在事务C(102)中,把数据从 value =1 改为 value =2,rowtrx_id时102提交的
在A(100)查询的时候 它的视图数组是 [99,100],所以读取数据要从当前版本开始读。
在可重复读隔离级别下, 只要事务开始读时候创建一致性视图,之后事务里的其他查询都 共用这个一致性视图
在读提交隔离级别下,每一个语句执行钱都会重新算出一个新的视图
-
读取流程
- 查询结果 rowtrx_id = 101 比高水位大,处于
未开始事务
,不可见版本 - 查询上一个历史版本,rowtrx_id = 102 ,处于
未开始事务
,不可见版本 - 查询上一个历史版本,rowtrx_id = 90 , 低水位,
已提交事务
,可见
- 查询结果 rowtrx_id = 101 比高水位大,处于
这样执行,虽然期间这一行数据被修改过,但是事务A不论在什么时候查询,看到这行数据的结果都是一致的,
事务的一致性
更新逻辑
事务B 的视图 [99,100,101] 并不知道 事务C (102) 的存在 ,是如何 更新出 (1,3)的了
事务B 在更新之前查询一次数据,这个查询值确实是 value = 1
但更新数据的时候,是不能在历史版本更新的
更新数据需要先读后写读当前数据(current read)
update 有当前读以外,select 加锁也是当前读
当前读是有锁的,确保一次只有一个事务操作当前版本。