mysql mvcc 多版本控制
MVCC 的作用是什么?
避免因为写锁的阻塞而造成读数据的并发阻塞问题。
它是通过保存数据的多个历史版本,根据版本号来决定数据是否对事务可见。在InnoDB内部中,会记录一个全局的活跃读写事务ID数组,其主要根据事务ID 大小用来判断事务的可见性。
所以它可以使数据不用加锁就达到事务隔离的效果。
事务版本号(row trx_id)
在InnoDB下,每个事务都有一个唯一的事务ID(transaction id),它是在事务开始的时候向事务系统申请(全局ID生成器)的,并且按照申请顺序严格递增.
每行数据都会有多个版本,每次事务更新数据的时候都会生成一个新的数据版本,并且把transaction id赋值给这个数据版本的事务id.
数据版本
每一次的插入及更新操作都会视为一个版本。这个版本的架构如下:
- trx_id。事务ID
- roll ptr。指向上一个数据版本的指针,用于回退版本
根据这两个字段可以形成一个单链表,使得数据都可以溯源;在查询的时候,就可以有选择性的展示哪个版本的数据。
一致性视图
InnoDB会为每个事务构造一个数组,该数组用来保存事务启动瞬间当前正在活跃(启动还未提交)的所有事务ID。
数组里面事务ID的最小值为低水位,当前系统里面已经创建过的事务ID的最大值加1为高水位。该视图数组和高水位就组成了当前事务的一致性视图。
对于当前事务的启动瞬间,一个数据版本的row trx_id会有以下几种可能:
- 如果在绿色部分,表示该版本是已提交的事务或者是自己生成的,数据可见
- 如果落在红色部分,表示该版本是由未来的事务生成的,数据不可见
- 如果落在黄色部分,如果row trx_id在数组中,表示该版本是由还没提交的事务生成,数据不可见;如果row trx_id不在数组中,表示该版本是已经提交了的事务生成的,可见。
下面来举个例子看下
捋一下 trx_id = 2 的事务是怎么读到数据的?
每次事务更新数据的时候都会生成一个新的数据版本,并且把transaction id赋值给这个数据版本的事务id.
即上述示例中最终版本的trx_id 为3。
启动trx_id=2的事务时一致性视图为 : 低水位2 ,高水位3。
- 找到 age = 3 版本的 trx_id = 3。判断 >= 当前事务的高水位,不可见。继续根据 ptr 找 age = 4 的版本。
- 找到 age = 4 版本的 trx_id = 4。判断 >= 当前事务的高水位,不可见。继续根据 ptr 找 age = 5 的版本。
- 找到 age = 5 版本的 trx_id = 5。判断 >= 当前事务的高水位,不可见。继续根据 ptr 找 age = 24 的版本。
- 找到 age = 24 版本的 trx_id = 1。判断 < 当前事务的低水位,可见。
这么一套下来,trx_id = 2 的事务 不管在什么时候查询,看到的数据都是 age = 24。这就是一致性视图。
update逻辑和select逻辑的不同
更新数据都是先读后写,并且这个读为当前读。
资料来源于
http://www.codeforest.cn/article/3019
https://zhuanlan.zhihu.com/p/460916152