深入理解 MySQL 事务 MVCC 的核心概念以及底层原理

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 在MVCC多版本并发控制中,核心概念和原理是非常复杂的,我们先来搞清楚MVCC中常见名称的基本概念,然后再来讲解什么是MVCC以及MVCC的原理。

MVCC多版本并发控制核心概念以及底层原理

1.当前读与快照读的基本概念

在MVCC多版本并发控制中,核心概念和原理是非常复杂的,我们先来搞清楚MVCC中常见名称的基本概念,然后再来讲解什么是MVCC以及MVCC的原理。

1.1.当前读的基本概念

当前读指的是在事务中,通过Select查询语句读取的数据记录是当前表中最新版本的记录,默认情况下,在事务中读取表中的数据时,为了避免并发事务对我们读取的数据进行修改,会对读取的记录加锁,即使其他事务修改了表中的数据,我们读取到的数据仍然是其他事务修改之前的数据。

即使在事务中,我们也想要读取当前表中最新的数据记录,而并不是进入事务时查询到的数据,那么此时就需要用到当前读的概念,突破事务一开始读取数据的锁,通过当前读来读取表中最新版本的数据记录。

如何才能突破读取表记录加的锁呢?很简单只要触发当前读的机制,使当前的查询语句进化成当前读的行为,就可以读到表中最新版本的数据,当事务中执行的SQL,如select lock in share mode、update、insert、delete、select...for update这些,产生了共享锁和排它锁,此时就会产生当前读。

下面来演示一下当前读的效果。

1.开启一个事务然后查询xscjb中的数据,看到小明的ywcj是100
mysql> begin;
mysql> select * from xscjb;
+----+--------+------+------+------+------+
| xh | xm     | ywcj | sxcj | yycj | pjcj |
+----+--------+------+------+------+------+
|  1 | 小明   |  100 |   75 |   93 | NULL |

2.此时再开启一个事务修改小明的ywcj并提交
mysql> begin;
mysql> update xscjb set ywcj = '999' where xm = '小明';
mysql> commit;

3.此时第一个事务任然读到的数据是当前事务进入时的数据状态,并非是最新的数据

4.如果想要读取表中最新的数据,那么就需要通过产生共享锁、排查锁的方式读取到
mysql> select * from xscjb lock in share mode;
+----+--------+------+------+------+------+
| xh | xm     | ywcj | sxcj | yycj | pjcj |
+----+--------+------+------+------+------+
|  1 | 小明   |  999 |   75 |   93 | NULL |

image.png

1.2.快照读的基本概念

快照读指的是:开启事务后第一次查询数据的结果集,这个结果集就会被做成快照读,只要还是在当前的事务中,即使数据被其他事务修改了,我们无论执行多少次查询,依旧查询到的是快照读的数据。

如1.1中的所示,即使ywcj被其他事务修改了,在当前事务中读到的仍然是旧数据,也就是快照读的数据。

简单的select产生的都是快照读,快照读取的是记录数据的可见版本,也有可能是历史数据,不加锁是非阻塞读。

在不同隔离级别下,快照读也不同:

  • Read Committed:每次select查询,都会生产新的快照读。
  • Repeatable Read:开启事务后第一个select查询,就是快照读的地方。
  • Serializable:快照读退化成当前读。

快照读也保证了数据的可重复读。

2.什么是MVCC多版本并发控制

MVCC全称是Multi-Version Concurrency Control,多版本并发控制,MVCC可以维护一个数据的多个版本,使读写操作没有冲突。

MVCC是一种并发控制的方法,有了MVCC的支持后,不再使用单纯的行级锁对数据库中的并发进行控制,而是使用MVCC将数据库中的行锁与行的多个版本进行结合,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。

并发控制也很好理解,有人通过事务读取了表中的数据,同时也有人通过事务在表中写入或修改了数据,就会导致多个人看到的数据是不一致的,通过并发控制的手段,使每个连接者,在某个瞬间看到的数据时一个快照,即使通过其他事务修改了表中的数据,对于读者来说也是看不到的,从而保证数据的一致性。

MVCC实现的是读写不阻塞,读写互不影响,通俗一点来说,MVCC可以使用户觉得数据库对于同一条数据,面对多个事务并发情况下,有多个不同版本的数据所提供。

MVCC多版本并发控制通过一定的机制生成一个数据请求时间点内,一致性的数据内容,也就是快照,并且利用这个快照来提供一定级别的一致性读取,使用户读写数据互不影响。

MVCC实现原理依靠于三个部分:隐式字段、undo log日志、ReadView。

3.MVCC多版本并发控制依赖的三个组件重要概念

我们知道什么是MVCC之后,接下来就需要去探讨MVCC多版本并发控制实现的原理了,再研究原理之前,先弄明白,MVCC依赖的隐式字段、undo log日志、ReadView是什么东西。

3.1.MySQL表中三个隐式字段的概念

当我们创建好一张数据表之后,除了表中所有的字段外,InnoDB存储引擎还会添加上三个隐藏的字段。

  • DB_TRX_ID

    • 表中的数据时会被修改的,INSERT、UPDATE、DELETE这些语句默认情况下,一条就代表一个事务,这个字段就是来记录最后一次修改本条数据的事务ID。
  • DB_ROLL_PTR

    • 该字段是指针,代表回滚指针,该字段值会记录本条数据上次修改前的一个版本,每条数据被修改后都会在undo log中进行记录,在undo log记录的每条数据中都会有一个版本号,该字段就是来记录本条数据上次修改前在undo log中的版本号,可以配合undo log日志进行数据的回滚。
  • DB_ROW_ID

    • 该字段可能会出现也可能不出现,主要取决于表中是否存在主键,如果表中没有主键,该字段就会出现,通过自增的方式为每条数据记录一个ID,主要是为聚集索引服务的。

我们查看一个有主键的表所包含的隐式字段,当表中有主键时,只会出现DB_TRX_ID和DB_TRX_ID隐式字段。

[root@k8s-master ~]# ibd2sdi /var/lib/mysql/db_1/xscjb.ibd | grep name
        "name": "xscjb",
                "name": "xh",
                "name": "xm",
                "name": "ywcj",
                "name": "sxcj",
                "name": "yycj",
                "name": "pjcj",
                "name": "DB_TRX_ID",                    #记录事务ID的隐式字段
                "name": "DB_ROLL_PTR",                    #记录回滚指针版本的隐式字段
                "name": "PRIMARY",
                "name": "idx_xscjb_ywcj",
        "name": "db_1/xscjb",
                "filename": "./db_1/xscjb.ibd",

我们查看一个没有主键的表所包含的隐式字段,当表中没有主键时,三个隐式字段都会出现。

[root@k8s-master ~]# ibd2sdi /var/lib/mysql/db_1/jszx_xgymjzxxb.ibd | grep name
        "name": "jszx_xgymjzxxb",
                "name": "id",
                "name": "bm",
                "name": "name",
                "name": "xb",
                "name": "nl",
                "name": "szd",
                "name": "zjhm",
                "name": "wd",
                "name": "first_injection",
                "name": "second_injection",
                "name": "third_injection",
                "name": "wjzymjtyy",
                "name": "zhycjzymdsj",
                "name": "DB_ROW_ID",                #记录行ID的隐式字段
                "name": "DB_TRX_ID",                #记录事务ID的隐式字段
                "name": "DB_ROLL_PTR",                #记录回滚指针版本的隐式字段
                "name": "PRIMARY",
        "name": "db_1/jszx_xgymjzxxb",
                "filename": "./db_1/jszx_xgymjzxxb.ibd",

3.2.undo log日志以及版本链的概念

undo log是回滚日志,当数据库中产生insert、update、delete操作时就会产生便于数据回滚的日志,该日志就是undo log。

undo log日志是可以被删除的,当产生insert语句后,事务一旦提交,undo log中的insert语句就可以被立即删除,因为undo log只会在回滚时用到,像update、delete语句则不会立即删除,因为还有可能其他事务再读取这些数据。

undo log日志是实现MVCC版本控制最核心的一点,undo log日志中的版本链为数据形成了一份不同内容版本的链,这些链都会记录在undo log日志文件中。

下面我们通过几幅图来演示undo log日志中的版本链的概念。

有一张表的原始数据如下,表中只有一条记录,DB_TRX_ID字段的值为1,因为是新表只有一条数据,那么对应的事务ID也就是1,DB_ROLL_PTR字段的值为NULL,新插入的数据没有被更新过,因此该字段的值为null。

id age name DB_TRX_ID DB_ROLL_PTR
30 30 A30 1 null

目前有四个并发事务(事务1是插入了这条数据,从事务2开始)同时操作这张表中的数据,我们来观察undo log会记录什么。

(undo log中记录的是sql语句,这里为了方便演示,以真实数据代替)

1)事务2:修改表中id为30的数据,将age的值修改为3,修改完成后提交事务。

如下图所示,当事务2中的修改语句执行时,首先将旧数据记录在undo log日志中,然后再去更新表中的记录,并且更新表中的数据时,会将隐式字段DB_TRX_ID的值更新成事务2的ID,同时回滚指针字段DB_ROLL_PTR的值也会指向undo log中记录的旧数据对应的版本号,用于将来回滚使用。

image.png

2)事务3:修改表中id为30的数据,将name字段修改为A3,修改完成后提交事务。

首先也是将变更前的数据记录到undo log日志文件中,此时的版本链不变,记录好之后,开始修改表中的数据,同时会将本条数据的DB_TRX_ID字段值修改成最后一次事务的ID,DB_ROLL_PTR的值会修改成undo log中记录的修改前的旧数据对应的版本号,此时链就发生了改变了,表中数据的DB_ROLL_PTR值指向了最新一次旧数据的版本号,那么undo log中最新一次旧数据表的版本号同样也会指向它上一次旧数据对应的版本号。

新数据指向最新一次旧数据的版本号,最新一次旧数据指向上次旧数据的版本号。

image.png

3)事务4:修改表中id为30的数据,将age字段修改为10,修改完成后提交事务。

此时版本链的编号和事务3基本一样了,首先在undo log中记录旧数据,然后修改新数据的内容,然后将DB_TRX_ID字段修改成最新事务的ID,将DB_ROLL_PTR指针指向上一次旧数据对应的版本号,undo log中的最新一次的旧数据,也会指向上一次旧数据对应的版本号。

image.png

最终我们可以看到在undo log中已经形成版本链了,不同事务或者相同事务操作一条记录时,会在undo log中为这条记录生产版本链表,链表的头部是最新的旧数据记录,链表的底部是最早的旧数据记录。

此刻我们的一条数据就对应了很多个不同版本的数据情况,那么如何来识别读哪一个版本的数据呢?就需要去了解ReadView了。

3.3.ReadView读视图的概念

ReadView读视图:是SQL产生了快照读时,生成一个ReadView作为MVCC读取数据的依据,我们知道当产生快照读时,读到的几乎都是历史数据,并不是最新数据,在undo log中记录的每一条旧数据记录都是历史数据,一条数据可能会对应很多个版本的历史数据,那么快照读在执行时究竟应该读取哪一个版本的数据呢?其实就是靠ReadView读视图来决定的。

在ReadView中有四个字段来记录不同类型事务的ID,并且这四个字段与undo log中的事务ID有相应的匹配规则,当满足某一项规则时,就会读取该规则对应的版本的历史数据。

ReadView读事务会依据以下四个字段判断要读取那一个版本的历史数据:

  • m_ids:是一个集合,当前活跃的所有读写事务的事务ID都会记录在这个ids集合中。

    • 活跃的事务就表示当前事务正在进行中,还没有提交,只要事务没有提交都会处于活跃的状态。
  • min_trx_id:最小活跃事务ID。

    • 是基于m_ids集合内的所有活跃的事务ID,在这个集合中最小的活跃事务ID。
  • max_trx_id:预分配事务ID,不是当前活跃的事务集合中最大的事务ID,相当于一个预留的事务ID,一般都是当前事务ID+1的新事务ID。
  • creator_trx_id:ReadView创建者的事务ID,通常情况也是当前事务的事务ID。
当前数据中DB_TRX_ID字段对应的事务ID,如果比最小的活跃事务ID还要小,就表示DB_TRX_ID字段对应的事务ID是处于提交的状态。如果DB_TRX_ID字段对应的事务ID比最小活跃事务ID还大,那么说明改事务可能也是活跃事务处于未提交的状态。

有了读取数据版本的依据之后,就需要有对应的规则来决定要读取数据的哪个版本。

ReadView决定要读取数据的哪个历史版本时,是由undo log版本链中数据对应的事务ID与ReadView的四个字段中记录的事务ID进行规则匹配,共有四种匹配规则,当匹配的结果满足规则时,就会读取规则对应的历史版本数据。

undo log版本链中针对相同的数据可能会记录很多条不同版本的数据,此时就会先拿表中当前的数据与ReadView规则进行匹配,如果满足则读取这个版本的数据,如果不满足则再去undo log版本链中从上往下一次匹配每一条数据,当满足ReadView规则时,则读取对应版本的数据,

在匹配ReadView规则时,是从上往下依次进行规则匹配的,当满足第一个规则时就读取对应的版本数据,后面的规则将不会匹配。读取数据肯定会被某一个规则所匹配。

ReadView读取某个版本数据时的四种匹配规则:

表中的一条数据或者undo log版本链中的一条数据,每一条数据都称为一个版本。匹配规则是先从表中的数据记录开始匹配,如果表中数据不满足规则时,再从undo log版本链中对于数据的多个版本,从上到下依次匹配,只要有一个版本满足了规则,则会返回该版本的数据,不会再往下进行匹配。

  • 当trx_id == creator_trx_id

    • 当某个版本的数据记录中DB_TRX_ID字段记录的事务ID,与ReadView的creator_trx_id字段记录的事务ID相同,那么就可以读取这个版本中的数据。
    • 因为creator_trx_id字段的值记录的是当前事务的ID,如果数据中的记录最后一次操作的事务ID值与creator_trx_id字段的值相同,就表示是当前事务所修改的数据,因此它是可以读取到最新版本的数据的。
  • 当trx_id < min_trx_id

    • 当某个版本的数据中记录事务ID值,小于所有活跃事务中最小的事务ID值时,就可以读取这个版本的数据。
    • 因为所有的活跃事务,不管ID是大还是小,都是活跃是未提交的事务,如果当前版本的数据事务ID与活跃的事务ID相等或者比它ID大,就说明这个版本的数据还有可能被其他的事务处理中,因此是不可以被访问到的。只有当版本中数据的事务ID比所有活跃事务中最小的那个事务ID还要小,就表示该版本的数据没有被事务使用了,已经是提交状态了,因此就可以读取这个版本的数据。
  • 当trx_id > max_trx_id

    • 当某个版本的数据中记录事务ID值,大于预留的事务ID,那么就不可以读取这个版本中的数据。
    • 如果当前版本中数据记录的事务ID比预分配的事务ID要大,那么就说明这个版本的数据是在我们当前事务之后又开启的新事务,数据在处理中,因此不可以读取这个版本中的数据。
  • 当min_trx_id <= trx_id <= max_trx_id

    • 当某个版本的数据记录中事务ID的值,大于最小活跃的事务ID值,也小于预留的事务ID值,也就是事务ID位于最小活跃事务ID和预留事务ID之间的ID,并且当前版本数据记录中的事务ID值不在m_ids集合中,当满足这个规则时,那么就可以读取这个版本中的数据。
    • 这个规则相当于给了一个事务ID的范围,最小事务ID---最大事务ID区间的事务ID,如果版本中数据的事务ID在这个范围列表里,还需要看一下这个事务ID是不是活跃的事务ID,主要是在m_ids集合中查看,如果也不会活跃的ID,那么就允许读取此版本的数据,否则将不允许。
在四种规则里,trx_id表示的是当前版本中数据对应的事务ID的值,要通过trx_id的值和ReadView四个字段对应的值,进行匹配,满足相应规则时放行。

在不同的隔离级别下,生成ReadView的时机不同:

  • READ COMMITTED :在事务中每一次执行快照读时都生成一个ReadView,每个ReadView中四个字段的值都是不同的。
  • REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

4.MVCC实现多版本并发控制的原理

MVCC多版本并发控制实现的原理就是通过InnoDB表的隐藏字段、undo log版本链、ReadView读视图配合来实现。

1)首先在表中的数据都会有两个主要的隐藏字段,一个是DB_TRX_ID记录最后一次操作的事务ID,还有一个是DB_ROLL_PTR记录版本指针用于回滚时使用。

2)在多并发事务的场景下,不同事务操作完表数据会都会将旧数据记录在undo log,一条数据在undo log日志中可能会多个不同版本的数据,最终形成版本链表,不同版本的数据通过DB_ROLL_PTR字段值相互关联。

3)当数据在undo log日志中形成版本链之后,MVCC就会通过ReadView读视图根据四个字段,与undo log版本链中不同版本中数据的事务ID进行规则匹配,当undo log版本链中从上到下的某一个版本数据满足ReadView读视图中的规则,那么就读取该版本对应的数据,并且不会再使用版本链中其他的版本数据再进行规则匹配。

简单来说,MVCC实现多版本并发控制的原理,就是根据多事务在undo log中产生的多条旧数据形成的版本链表,将一条数据的多个版本中的事务ID与ReadView读视图中的四个字段所对应的事务ID进行规则匹配,如果这个版本的数据满足ReadView四个字段的规则,那么就读取这个版本的数据,如果不满足规则,则用另一个版本的数据依次进行匹配,知道读到满足规则的数据。

多版本并发控制就是在多事务的场景下,读取针对当前事务最合适的一个版本的数据,可能是新数据也可能是旧数据。

MVCC+锁就实现了事务的隔离性,事务的一致性由ReadLog和UndoLog保证。

5.不同隔离级别下MVCC实现并发控制的原理

5.1.RC隔离级别下MVCC多版本并发控制的原理分析

在RC隔离级别下,每当执行的SQL是快照读类型的,就会生成一个ReadView读视图,每次生成的ReadView读视图所对应的四个字段值都是不同的,在RC隔离级别下,每次快照读读取的版本数据可能也不相同。

下面我们通过一组事务来分析RC隔离级别下MVCC多版本并发控制的原理。

如下图所示,在并发事务5中,查询了两次id为30的数据,由于当前的隔离级别是RC,所以每当产生一次快照读都会生成一个ReadView,每次生成的ReadView四个字段值都不同,也就意味着两次查询相同数据的结果可能都不相同。两次快照读在获取数据时,会根据所生成的ReadView四个字段的值与undolog版本链中的数据进行规则匹配,最终返回此次快照读的数据。

image.png

ReadView四个字段的获取的值:m_ids记录所有活跃事务的id号,分别是3/4/5对应事务3-事务5,min_trx_id记录所有活跃事务中事务ID最小的值,那么也就是3,max_trx_id是预留的事务ID,当前事务ID是5,那么预留的事务ID就是6,creator_trx_id是生成readview的事务id,也就是5。

1)分析事务5中第一次快照读的MVCC多版本并发控制的原理流程

如下图所示,左侧是快照读可能会读取的各个版本的数据,有表中的记录,有undo log版本链中的记录,右侧是ReadView读取版本数据的规则,并且将第一个快照读产生的ReadView四个字段的值,带入到了规则中,下面开始匹配。

image.png

A)在匹配合适的版本数据时,首先匹配表中的记录:image.png
也就是这条数据,这条数据对应的trx_id事务id是4,此时MVCC就会通过ReadView带着这条数据去ReadView规则中进行匹配,在第一条规则中,trx_id为4不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=4大于了min_trx_id(ID为3),第三条规则中trx_id=4小于了max_trx_id(ID为6),第四条规则,trx_id=4位于min_trx_id(ID为3)与max_trx_id(ID为6)之间,但是该版本的数据事务ID是4,4位于m_ids(ID:3,4,5)集合中。

规则匹配结果为:1)不满足 2)不满足 3)不满足 4)不满足,该版本的数据都不满足规则,此时就要去undo log版本链中匹配下一条数据了。

表中数据不满足了,此时从undo log链中从上往下挨个匹配每个版本的数据,当某一个版本数据满足规则后,下面的数据不再进行匹配。

B)然后匹配undo log版本链中最上面的数据:image.png
,该版本数据的trx_id事务ID为3,将trx_id=3带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为3不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=3等于了min_trx_id(ID为3),第三条规则中trx_id=3小于了max_trx_id(ID为6),第四条规则,trx_id=3位于min_trx_id(ID为3)与max_trx_id(ID为6)之间,但是该版本的数据事务ID是3,3位于m_ids(ID:3,4,5)集合中。

规则匹配结果为:1)不满足 2)不满足 3)不满足 4)不满足,该版本的数据都不满足规则,此时继续从undo log版本链中从上到下匹配下一条数据。

C)接着匹配undo log版本链中第二条版本数据:
image.png,该版本数据的trx_id事务ID为2,将trx_id=2带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为2不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=2小于min_trx_id(ID为3),该版本的数据满足ReadView规则中的第二个规则,此时就会终止匹配,快照读此时就会返回版本链中这个版本所对应的数据。

表中记录、undo log版本链的数据从上往下依次匹配ReadView规则,当有一个版本的数据满足规则后,就返回给快照读获取该版本的数据,这就是MVCC多版本并发情况下,分配给快照读合适版本数据的原理和过程。

2)分析事务5中第二次快照读的MVCC多版本并发控制的原理流程

在第一次快照读时我们已经理解了MVCC是如何实现多版本并发控制的,根据表中记录、undo log版本链中多个不同版本的数据,按照数据中的事务ID在ReadView规则中进行匹配,当满足规则时,将该版本的数据返回给快照读。

第二次快照度和第一次快照度大差不差,在第二次快照读时,事务3提交了,那么在活跃的事务中就没有事务3了,数据还是左侧这么多个版本,右侧的规则中为此次生成的ReadView四个字段带入了新值。

image.png

ReadView四个字段的获取的值:m_ids记录所有活跃事务的id号,分别是4/5对应事务4-事务5,min_trx_id记录所有活跃事务中事务ID最小的值,那么也就是4,max_trx_id是预留的事务ID,当前事务ID是5,那么预留的事务ID就是6,creator_trx_id是生成readview的事务id,也就是5。

A)首先匹配表中的记录:image.png
也就是这条数据,该版本数据的trx_id事务ID为4,将trx_id=3带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为4不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=4等于了min_trx_id(ID为4),第三条规则中trx_id=4小于了max_trx_id(ID为6),第四条规则,trx_id=4位于min_trx_id(ID为4)与max_trx_id(ID为6)之间,但是该版本的数据事务ID是4,4位于m_ids(ID:4,5)集合中。

规则匹配结果为:1)不满足 2)不满足 3)不满足 4)不满足,该版本的数据都不满足规则,此时就要去undo log版本链中匹配下一条数据了。

B)然后匹配undo log版本链中最上面的数据:image.png
,该版本数据的trx_id事务ID为3,将trx_id=3带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为3不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=3小于min_trx_id(ID为4),该版本的数据满足ReadView规则中的第二个规则,此时就会终止匹配,快照读此时就会返回版本链中这个版本所对应的数据。

5.2.RR隔离级别下MVCC多版本并发控制的原理分析

在RR隔离级别下,只会在事务第一次执行快照读时会生成一个ReadView读视图,后续快照读都会复用这个ReadView,读取的版本数据都是相同的,也就说明了RR隔离级别是可重复度。

下面我们通过一组事务来分析RR隔离级别下MVCC多版本并发控制的原理。

如下图所示,在并发事务5中,查询了两次id为30的数据,由于当前的隔离级别是RR,所以当第一次产生快照读会生成一个ReadView决定四个字段的值,后面再有快照读执行时,就会复用第一次快照读产生的ReadView,也就意味着每次快照度产生的结构都是一样的。

image.png

ReadView四个字段的获取的值:m_ids记录所有活跃事务的id号,分别是3/4/5对应事务3-事务5,min_trx_id记录所有活跃事务中事务ID最小的值,那么也就是3,max_trx_id是预留的事务ID,当前事务ID是5,那么预留的事务ID就是6,creator_trx_id是生成readview的事务id,也就是5。

多个版本的数据在RR隔离级别下的规则匹配流程与RC隔离级别一致。

1)分析事务5中首次快照读的MVCC多版本并发控制的原理流程

如下图所示,左侧是快照读可能会读取的各个版本的数据,有表中的记录,有undo log版本链中的记录,右侧是ReadView读取版本数据的规则,并且将第一个快照读产生的ReadView四个字段的值,带入到了规则中,下面开始匹配。

image.png

A)首先匹配表中的记录:image-20220629112802451也就是这条数据,这条数据对应的trx_id事务id是4,此时MVCC就会通过ReadView带着这条数据去ReadView规则中进行匹配,在第一条规则中,trx_id为4不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=4大于了min_trx_id(ID为3),第三条规则中trx_id=4小于了max_trx_id(ID为6),第四条规则,trx_id=4位于min_trx_id(ID为3)与max_trx_id(ID为6)之间,但是该版本的数据事务ID是4,4位于m_ids(ID:3,4,5)集合中。

规则匹配结果为:1)不满足 2)不满足 3)不满足 4)不满足,该版本的数据都不满足规则,此时就要去undo log版本链中匹配下一条数据了。

B)然后匹配undo log版本链中最上面的数据:image.png
,该版本数据的trx_id事务ID为3,将trx_id=3带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为3不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=3等于了min_trx_id(ID为3),第三条规则中trx_id=3小于了max_trx_id(ID为6),第四条规则,trx_id=3位于min_trx_id(ID为3)与max_trx_id(ID为6)之间,但是该版本的数据事务ID是3,3位于m_ids(ID:3,4,5)集合中。

规则匹配结果为:1)不满足 2)不满足 3)不满足 4)不满足,该版本的数据都不满足规则,此时继续从undo log版本链中从上到下匹配下一条数据。

C)接着匹配undo log版本链中第二条版本数据:image.png
,该版本数据的trx_id事务ID为2,将trx_id=2带入右侧的ReadView版本链中进行匹配,在第一条规则中,trx_id为2不等于creator_trx_id(ID为5)的ID值,第二条规则中,trx_id=2小于min_trx_id(ID为3),该版本的数据满足ReadView规则中的第二个规则,此时就会终止匹配,快照读此时就会返回版本链中这个版本所对应的数据。

在RR隔离级别下,首次快照读读的版本数据,在后续的快照读中也会复用该数据,做到重复读。
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
存储 关系型数据库 MySQL
MySQL MVCC全面解读:掌握并发控制的核心机制
【10月更文挑战第15天】 在数据库管理系统中,MySQL的InnoDB存储引擎采用了一种称为MVCC(Multi-Version Concurrency Control,多版本并发控制)的技术来处理事务的并发访问。MVCC不仅提高了数据库的并发性能,还保证了事务的隔离性。本文将深入探讨MySQL中的MVCC机制,为你在面试中遇到的相关问题提供全面的解答。
55 2
|
25天前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
95 43
|
22天前
|
存储 关系型数据库 MySQL
MySQL主从复制原理和使用
本文介绍了MySQL主从复制的基本概念、原理及其实现方法,详细讲解了一主两从的架构设计,以及三种常见的复制模式(全同步、异步、半同步)的特点与适用场景。此外,文章还提供了Spring Boot环境下配置主从复制的具体代码示例,包括数据源配置、上下文切换、路由实现及切面编程等内容,帮助读者理解如何在实际项目中实现数据库的读写分离。
MySQL主从复制原理和使用
|
19天前
|
存储 关系型数据库 MySQL
MySQL MVCC深度解析:掌握并发控制的艺术
【10月更文挑战第23天】 在数据库领域,MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种重要的并发控制机制,它允许多个事务并发执行而不产生冲突。MySQL作为广泛使用的数据库系统,其InnoDB存储引擎就采用了MVCC来处理事务。本文将深入探讨MySQL中的MVCC机制,帮助你在面试中自信应对相关问题。
57 3
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1625 14
|
22天前
|
SQL 关系型数据库 MySQL
Mysql中搭建主从复制原理和配置
主从复制在数据库管理中广泛应用,主要优点包括提高性能、实现高可用性、数据备份及灾难恢复。通过读写分离、从服务器接管、实时备份和地理分布等机制,有效增强系统的稳定性和数据安全性。主从复制涉及I/O线程和SQL线程,前者负责日志传输,后者负责日志应用,确保数据同步。配置过程中需开启二进制日志、设置唯一服务器ID,并创建复制用户,通过CHANGE MASTER TO命令配置从服务器连接主服务器,实现数据同步。实验部分展示了如何在两台CentOS 7服务器上配置MySQL 5.7主从复制,包括关闭防火墙、配置静态IP、设置域名解析、配置主从服务器、启动复制及验证同步效果。
Mysql中搭建主从复制原理和配置
|
30天前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
8天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
22 4
|
6天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
19 1
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
78 1