MVCC机制与Buffer Pool

在上一篇文章《事务隔离级别与锁机制》中介绍了,读已提交和可重复度隔离级别都用到了MVCC机制,MySQL在可重复读的事务隔离级别下,可以解决数据的不可重复读问题,就算其他事务对访问的数据进行了修改,也不影响当前事务两次读取的结果。而这个隔离性就是由**MVCC(Multi-Version Concurrency Control)**机制来保证的,对一行数据的读写操作不要进行加锁就可以保证隔离性,达到了一致性非锁定读的效果。
注:MySQL在读已提交和可重复读两种事务隔离级别下都实现了MVCC机制

这篇文章就简单介绍一下MVCC机制是如何实现的,顺带简单介绍一下Buffer Pool,后面还有文章详细介绍。

一、MVCC

MVCC机制可以在不加锁的情况实现事务的隔离性,其基本原理就是不同的事务持可以看见不同版本的记录,而不同版本的记录就与undo log有着密切的关系,所以在介绍MVCC之前,需要先对undo log有个初步的了解,关于undo log的具体介绍,可以参考后面的文章。

1.1 undo log

undo log用于事务的回滚,可以利用undo log将数据恢复到修改之前的样子。
undo日志存放在数据库内部的一个特殊段(segment)中,这个段称为undo段,undo段位于共享表空间内(也可以配置存在undo表空间中)。
undo log是逻辑日志,并不是物理日志,因此只能将数据库逻辑的恢复到原来的样子,但数据结构和页在回滚之后可能大不相同。
注:事务在undo log segment分配页并写入undo log的过程中,同样需要写 redo log

除了回滚操作,undo的另一个作用就是MVCC,MVCC是通过undo log的版本链来实现的。关于undo 段的内容,可以参考后面介绍索引结构的文章。

1.2 undo log版本链

在MySQL的数据表中,对于每行数据而言,不仅记录了定义的字段值,还隐藏记录了两个字段:row_trx_idroll_pointer,前者表示更新该行数据的事务ID,后者表示的是回滚指针。它指向的是该行数据上一个版本的undo log
在Innodb存储引擎中,undo log分为两类:

  • insert undo log
  • update undo log

insert undo log是当前事务执行insert时产生的,它只对当前事务可见,而对其他事务是不可见的,所以当前事务提交时,可以直接删除insert undo log。而update undo log包含了updatedelete操作产生的undo log。该undo log可能需要提供MVCC机制,也就是可能还有其他已经开启的事务需要访问这些修改的记录。因此,当前事务在提交时,不能直接删除undo log,需要将其放入到undo log版本链中。

1.2.1 undo log行结构
1.2.2 undo log版本链结构

account表为例,其表结构如下:

CREATE TABLE account(
	id int NOT NULL AUTO_INCREMENT,
	name VARCHAR(255) DEFAULT NULL,
	balance INT DEFAULT NULL,
	PRIMARY KEY(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

下图展示了一个undo log版本链的结构,这也是实现MVCC机制的基本要素:

注:每条记录都对应一个undo log版本链,如果某条记录上的事务都提交了,那么会由后台线程去清理对应的undo log版本链。

1.3 read view(一致性试图)

有了undo log版本链之后,就可以做到每行记录可以有多个版本的记录,但对于一个事务来说,那些是可见的,哪些是不可见的,这就是read view的作用了。

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

1.3.1 read-view描述

按照read-view的描述,可以将事务分成三个范围,根据这三个范围,可以判断版本链的数据是否对当前事务可见。

虽然视图是由未提交事务的事务id数组组成,但从上面的图可以看出,黄色区域还包含了已提交事务,这是因为最大的事务ID对应的事务,可能是已经提交的事务。

1.3.2 版本链比对规则

undo log版本链有了,read-view一致性视图也有了,那么就可以判断哪些记录是可见的,哪些是不见的,对此有相应的判断规则。

当前事务已经创建了read-view,在读取数据的时候,就需要根据版本链中的事务id进行判断,以此来决定undo log的数据是否可见,比对规则有如下几种:
1、如果undo记录的trx_id位于read-view的绿色部分(trx_id < min_trx_Id),表示这个版本是已提交的事务生成的,所以数据可见。

2、如果undo记录的trx_id落在粉红色区域(trx_id > max_trx_id),表示当前版本是由将来启动的事务生成,是不可见的。(如果数据行的trx_id就是当前事务的trx_id,那么数据是可见的)

3、如果undo记录的trx_id落在黄色区域(min_trx_id =< trx_id =< max_trx_id),那就有两种情况:

  • 如果trx_id位于未提交事务ID数组中,表示这个版本是由还未提交的事务生成的,因此,不可见。(如果trx_id就是当前事务的id,则可见)
  • 如果trx_id不在未提交事务ID数组,说明这个版本是由已提交的事务生成,也就是trx_id为一致性视图中最大的事务ID,且该事务已经提交了,因此可见。

1.4 read-view比对流程

read-view在可重复读和读已提交两种隔离级别下,其实现是有些不同的,下面分别就这两个隔离级别,细看一下它们的比对流程。

1.4.1 可重复读隔离级别

分别开启了三个事务,然后在不同阶段执行三次相同的查询:

如表格所示,在查询前,开启了三个事务,其中trx_id为300的事务在查询前就提交了,于是,可以根据查询依次展示undo log的版本链,每次查询对应的undo log版本链都有所不同。

第一次查询:

第二次查询:

第三次查询:

这三次查询的比对流程如下:

因为是在可重复读隔离级别,所以第一次查询时生成了read-view后就不会再变化,第一次查询时,生成的一致性视图为[100,200],300,其中数组中为未提交事务,300为当前最大事务。
1、第一次查询时,undo 链只有一条记录,且记1录的trx_id为当前落在数组之外,所以,数据可以读
2、第二次查询时,最新记录的trx_id为100,在未提交事务ID数组里面,数据不可见;于是继续向下遍历,然后事务id为300的记录可见
3、第三次查询时,与第二次查询一样,前两次比对的记录的事务id都在未提交事务id数组中,数据均不可见,所以,最后读取到的还是事务id为300的记录数据

1.4.2 读已提交隔离级别

开启了三个事务,然后在不同阶段执行三次相同的查询,在读已提交隔离级别中,每次查询都会生成新的read-view
在这里插入图片描述

三次查询是的undo log版本链与上面可重复读隔离级别的undo log版本链是一致的。查询的比对流程如下:

第一次查询和第二次查询时,由于没有新的事务提交,所有read-view没有发生变化,读取的数据也都是事务id为300的那一条数据
而在第三次查询时,前面事务100提交了,于是第三次查询的read-view就变成了[200],300,根据比对规则,在对undo链上第二条事务id为100的记录进行比对时,发现它比未提交事务id数组中所有的事务id都小,说明这是一个已提交的事务,所以可见。于是该版本的数据就是查询的数据。
注:正是因为读已提交隔离级别每次查询都要生成新的一致性试图,才导致不可重复读问题

二、Buffer Pool

在InnoDB存储引擎中,一条更新的SQL语句,其详细的执行流程如下:
在这里插入图片描述

Innodb存储引擎中,MySQL对于数据的操作并不是直接写磁盘的。而是通过BufferPool的缓存机制来实现。
以下面的SQL为例:

update account set name = 'lihehe' where id =1

数据的更新操作分为以下几步:
1、加载id=1的记录所在页的整页数据到BufferPool
2、将旧值写入到undo log中,便于回滚
3、SQL执行器按照条件更新缓存中的数据
4、执行器将redo log写入到redo log buffer
5、准备提交事务,redo log写入到磁盘中
6、准备提交事务,binlog 写入到磁盘(用来数据恢复)
7、binlog写入完成后,写入commmit标记到redo log中。提交事务完成,该标记为了保证事务提交后redo与binlog的数据一致性
8、最后由后台线程将BufferPool中的数据以页为单位随机写入到磁盘

注:这里只是简单的介绍一下Buffer Pool,后面还会有大量文件,详细介绍这些步骤的操作。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值