mysql - 第11课 - MVCC多版本并发控制机制原理 看了就懂

导图(用于总结和复习)

MVCC (Multi-Version Concurrency Control)(多版本并发控制机制)

Mysql在可重复度事务隔离级别下,同样的sql在同一事务下多次执行查询结果相同,就算有其他事务修改数据,也不会影响当前事务的查询结果。这个隔离性就是靠MVCC(Multi-Version Concurrency Control)机制来保证的。

Mysql在读已提交可重复读隔离级别下都实现了MVCC机制。但是实现原理不大一样,在读已提交级别是每次select生成一个版本,在可重复读是第一次select生成一个版本。

undo日志版本链与read view机制详解

undo日志也叫回滚日志。innorDb 默认会在表中加3个隐藏字段:

1.DATA_TRX_ID:最近更新这条数据的事务ID,占6字

2.DATA_ROLL_PTR:存放指向上一个事务版本的指针,占7字节(版本链依赖于这个字段)

3.DB_ROW_ID:主键id,占6字节(表没有设置主键时会自动生成一个隐藏的主键id列)

一条数据被 增、删、改 操作后都会生成一条记录存放在 undo log 中(注意读不会生产记录),然后用DATA_ROLL_PTR指向上一个版本,形成一个历史版本链。mysql 结构图 (1).png

        read view(一致性视图。在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成),这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_trx_id,创建当前事务的id为 creator_trx_id)、已创建的最大事务id(max_trx_id),事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。

undo日志版本链与read-view对比规则如下:

  1. 如果被访问的事务id与 creator_trx_id 相等,则说明此时查询的是自己修改过的记录,返回该记录。(被访问id = 当前事务id,可用)
  2. 如果被访问的事务id比 min_trx_id 要小,则说明该事务在当前事务生成 read-view 前已提交,可以返回该记录。(被访问id < 最小id,可用)
  3. 如果被访问的事务id比 max_trx_id 要大,则说明该事务在当前事务生成 read-view 之后才开启,需要在undo日志版本链里继续对比下一条事务。(被访问id > 最大id,不可用)
  4. 如果被访问的事务id在min_trx_id max_trx_id 之间,先检查该事务id是否包含在 read-view 中,如果没包含,说明该事务已提交,可用返回记录。如果包含在 read-view 中,那么说明该事务未提交,继续对比下一条事务。(如果 最小id<被访问事务id<最大id,先判断是否在read-view中,如果在则可用,不走则继续对比)。

如果这一步看不懂,看了下一步的实例讲解就明白了

实例讲解

测试数据脚本:

CREATE TABLE `t_version` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ver` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

insert into t_version(ver) values (1);

下面从一个实例开始讲解MVCC的过程。注意当前隔离级别为可重复度

image.png

MySQL中有三种开启事务的方式:

  1. begin
  2. start transaction
  3. start transaction with consistent snapshot

前两种方式,MySQL执行后不会马上创建视图,是在执行第一条select语句时才会创建,第三种方式MySQL执行后会马上创建视图。

假设初始化表t_version ,id=1, ver=1 的事务id为1

在T4时刻,事务8修改修改了ver = ver + 1,此时ver=2。于是 undo log 日志版本链记录如下:

未命名文件 (2).png

在T6时刻,事务5查询id=1的记录。事务5开启事务时事务8还未启动,所以事务5的read-view为[3, 5]。
此时read-view中的参数为:
最小事务id:min_trx_id = 3
创建当前事务id:idcreator_trx_id = 5
最大事务id:max_trx_id = 5
接下来按照上面说的undo日志版本链与read-view对比规则进行对比:
第1步:被访问的事务id:DATA_TRX_ID=8,比最大事务id:max_trx_id=5要大 ,满足第三条,继续对比下一条日志。
第2步:被访问的事务id:DATA_TRX_ID=1,比最小事务id:min_trx_id = 3要小,满足第二条,返回DATA_TRX_ID=1的记录。

在T7时刻,事务5修改修改了ver = ver + 1,注意修改操作与快照无关,那个是由写锁控制的,想明白写锁看我上一篇。所以此时ver=3。于是 undo log 日志版本链记录如下:

未命名文件 (3).png

在T8时刻,事务5查询id=1的记录。因为是可重复度级别,事务5的read-view保持不变为[3, 5]。
此时read-view中的参数为:
最小事务id:min_trx_id = 3
创建当前事务id:idcreator_trx_id = 5
最大事务id:max_trx_id = 5
接下来按照上面说的undo日志版本链与read-view对比规则进行对比:
第1步:被访问的事务id:DATA_TRX_ID=5,创建当前事务id:idcreator_trx_id = 5 ,满足第一条,查询的这条记录有被自己修改过,直接返回DATA_TRX_ID=5的记录。

在T9时刻,事务3查询 id=1 的记录。事务3开启事务时的read-view为[3]。
此时read-view中的参数为:
最小事务id:min_trx_id = 3
创建当前事务id:idcreator_trx_id = 3
最大事务id:max_trx_id = 3
接下来按照上面说的undo日志版本链与read-view对比规则进行对比:
第1步:被访问的事务id:DATA_TRX_ID=5,比最大事务id:max_trx_id = 3要大 ,满足第三条,说明该被访问的事务在当前事务生成read-view之后开启,继续对比下一条事务。
第2步:被访问的事务id:DATA_TRX_ID=8,同上
第3步:被访问的事务id:DATA_TRX_ID=1,比最小事务id:min_trx_id = 3要小,满足第二条,返回DATA_TRX_ID=1的数据。

看到这里相信原理都会了。另外mysql会判断把不需要的 undo log 清理掉,不会一直积累undo日志撑爆磁盘的。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值