美文网首页
MySql事务实现的机制:MVCC

MySql事务实现的机制:MVCC

作者: zekers | 来源:发表于2020-02-29 23:06 被阅读0次

MySql事务实现的机制:MVCC

这一篇将简单说明一下最近学习了Mysql的事务实现的简单理解。

如果存在一张A表,id=1,name=2,此时,存在多个事务对该表进行处理,那么会怎样呢?

假设我们会存在多个事务同时进行,

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7
8
9

首先运行第2行sql

当程序运行到第2行的时候,由于事务1对A表进行了更新操作,此时会产生一个事务id,我们假设该id的值为2。其中事务id是一个自增长的id,仅当事务中进行了更新操作的时候生成,同时该事务id整个数据库共用。

此时,mysql在一个叫做undo的回滚日志栈中存在着一条旧有的数据,在更新操作执行的时候,会押入当前更新的数据,同时记录下当前的事务id,并且产生一条指向前面押入的旧有数据的回滚指针。

即此时,回滚日志栈的结构如下


image-20200229213312983.png

同理,我们接下来运行第3行sql

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1

由于事务3运行了新的更新语句,一个新的事务id3生成,并且押入了undo中,即此时结构如下

image-20200229213727288.png

同理,运行第4,5行

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit

之后结构如下

image-20200229213944090.png

运行第6行

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1

我们可以看到,这一行的操作是查询操作。对于该查询操作,我们如何保证该操作的正确性?

当第6行执行查询操作的时候,会生成一个叫做read-view的一致性视图,该视图的结构如下

  • [min_id,min_id+1,...]-max_id

如上图所示,该视图的结构由一个id数组和一个id组成;

其中,数组包含了所有未提交的事务的id,而其中min_id是未提交的最小事务id。max_id则是已经创建的最大事务id,该id不关心是否已经提交。

那么,结合我们的事务的执行情况,则第六行生成的read-view为[2,3]-4。

接下来我们看图

image-20200229220109912.png

根据min_id和max_id的区分,我们将每个事务id(trx_id)区分为;

  1. 已经提交的事务id;(trx_id<min_id)
  2. 未提交或者可能提交了事务id;(min_id<=trx_id<=max_id)
  3. 当前查询语句之后才开始的事务id(max_id<trx_id)

然后,通过使用生成的read-view,从undo栈的栈顶开始逐行查询,之后通过以下规则鉴定可见性,如果当前查询行的数据可见,将返回对应的结果。

一、 查询行的版本号如果小于min_id,即为已经提交的事务(trx_id<min_id),那么这个数据是可见的。
二、 查询行的版本号如果大于max_id,即为已经提交的事务(max_id<trx_id),那么这个数据是不可见的。
三、 查询行的版本号如果处于min_id<=trx_id<=max_id的区间内,包括两种情况:
   1. 如果查询行的trx_id不在数组中,说明这个版本是已经提交了的事务,那么这个数据可见
   2. 如果查询行的trx_id在数据中,说明这个版本是还没提交的事务,那么:如果该版本不是当前自己事务的版本号,不可见,如果该版本是当前自己事务的版本号,可见。

结合上面的规则,我们来看这条查询语句:

  1. 语句执行后,read-view=[2,3]-4
  2. 查询栈顶,该行的版本号为4,即属于第三种情况,并且该事务已经提交,不在数组中,可见,返回结果:name4

运行第7,8行

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1

此时undo结构如下

image-20200229222812963.png

运行行数9

0 事务2 事务3 事务4 查询1 查询2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1

第9行我们在查询1中,运行了新的查询语句,我们来看这条查询语句

image-20200229223451610.png
  1. 语句执行后,read-view=[2,3]-4
  2. 从栈顶开始查询,该行的版本号为2,则属于第三种情况,同时存在于数组中,即该行数据不可见
  3. 继续查询下一行,结果同2
  4. 继续查询下一行,版本号为4,不存在于数组中,可见,返回结果name4

运行第10,11,12行

0 事务2 事务3 事务4 查询1 查询2
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1
10 commit; Update A set name='name3-1' where id=1
11 Update A set name='name3-2' where id=1
12

这行我们提交了事务2,而且事务3进行了两次更新,此时数据格式如下:

image-20200229224639347.png

运行第11行

0 事务2 事务3 事务4 查询1 查询2
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1
10 commit;
Select name from A where id = 1 Select name from A where id = 1

这一行我们将查询1和查询2放置在一起进行比较。

首先我们看查询2的语句:

image-20200229224719402.png
  1. 查询语句生成read-view=[3]-4
  2. 从栈顶开始查询,该行的版本号为3,则属于第三种情况,同时存在于数组中,即该行数据不可见
  3. 继续查询下一行,结果同2
  4. 继续查询下一行,版本号为2,不存在于数组中,可见,返回结果name2-1

接下来,我们看查询1的查询情况:

image-20200229225016097.png

如上图所示,mysql的事务分为可重复读,与不可重复读的情况。

如果是重复读的情况,每次查询将使用第一次生成的read-view;而如果是不可重复读的情况,那么每次查询都将使用新的read-view。即如果是可重复,处理情况如下

  1. 查询语句生成read-view=[2,3]-4
  2. 从栈顶开始查询,该行的版本号为3,则属于第三种情况,同时存在于数组中,即该行数据不可见
  3. 继续查询下一行,结果同3
  4. 继续查询下一行,版本号为2,则属于第三种情况,同时存在于数组中,即该行数据不可见
  5. 继续查询下一行,结果同4
  6. 继续查询下一行,版本号为4,即属于第三种情况,并且不存在于数组中,即该行数据可见,返回数据为name4。

由上面的情况即可以发现,两个查询事件就算同一时间查询,结果也是可能不同的。

对于查询的版本号小于min_id和大于min_id的情况,相对简单这里就不做讨论了。

而删除操作,在mysql中视作一种特殊的更新操作,会复制原来的数据,并更新状态为已删除,然后压入undo栈中。

相关文章

  • MySql事务实现的机制:MVCC

    MySql事务实现的机制:MVCC 这一篇将简单说明一下最近学习了Mysql的事务实现的简单理解。 如果存在一张A...

  • MySQL相关知识点

    MySQL(5)| 五分钟搞清楚 MVCC 机制MySQL (三) | 五分钟搞清楚MySQL事务隔离级别

  • Mysql MVCC机制

    概述 事务的ACID特性里面,Mysql通过MVCC机制来保证隔离性,实现可重复读的特性,但是可能会出现幻读的情况...

  • 五、MVCC的InnoDB实现

    MVCC是为了保证事务的非阻塞读,提高并发性的一种机制。目前有两种主流实现; 前置问题 mysql的事务是一开始就...

  • MVCC 隔离级别 视图

    MVCC Mysql/Oracle等数据库 均实现了MVCC(多版本并发控制),但各自的实现机制不尽相同。 Mys...

  • mysql innodb高并发基础-MVCC

    mysql innodb能高效运行,支撑高并发原因就是基于MVCC实现。 本文仅是简单介绍下MVCC原理,介绍事务...

  • 【Mysql】从事务到MVCC

    [TOC] 前言 本文主要介绍Mysql的事务特性以及mysql的MVCC机制,这块也是笔者在之前面试中的高频问题...

  • InnoDB的MVCC机制

    在讲解InnoDB的MVCC机制之前,我们应该了解MySQL所支持的事务,以及各个事务级别的区别和每一个事务级别所...

  • 深入理解MVCC与BufferPool缓存机制

    MVCC机制 MVCC(Multi Version Concurrency Control),MySQL(默认)R...

  • 数据库系列6-事务和锁

    MySQL利用MVCC(MVCC又是依据undo log实现的),在一个可重复读的事务执行过程中,读取到的数据都是...

网友评论

      本文标题:MySql事务实现的机制:MVCC

      本文链接:https://www.haomeiwen.com/subject/tymwhhtx.html