美文网首页
【MySQL】8|事务到底是隔离的还是不隔离的

【MySQL】8|事务到底是隔离的还是不隔离的

作者: 学而思之 | 来源:发表于2022-01-13 09:18 被阅读0次

一方面事务是可重复读,即整个事务期间看到的都是事务刚刚启动时候的视图;但是又因为行锁,事务在执行过程中可能需要等待别的事务更新同一行,那这个时候如果该事务也要更新,看到的还是事务刚刚创建时事务的状态吗?显然不是,这里的关键就是对于更新操作来说,需要当前读。

1、begin/start transaction 命令并不是一个事务的起点,在执行他们之后的第一个操作InnoDB表的语句,事务才真正启动。

2、如果要马上启动一个事务,可以使用start transaction with consistent snapshot 命令来实现。

  • 第一种启动方式

一致性视图是在执行第一个快照读语句时创建的。

例如:

事务A 事务B
begin;
update t set k = 100 where id = 100;
update t set k = 200 where id = 200;
select * from t;
commit;

事务A的select查询能够看到事务B的更新结果。

表明事务启动的时候,一致性视图还未创建,即update会启动事务,但是不会创建一致性视图。

  • 第二种启动方式

一致性视图是在执行 start transaction with consistent snapshot 时创建的。

例如:

事务A 事务B
start transaction with consistent snapshot;
update t set k = 100 where id = 100;
update t set k = 200 where id = 200;
select * from t;
commit;

事务A的select查询就无法看到事务B的更新结果。表明事务和一致性视图是一起创建的。

MySQL视图的概念

  • 一个是view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
  • 另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。

它没有物理结构,作用是事务执行期间用来定义“我能看到什么数据”。

“快照”在 MVCC 里是怎么工作的?

在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库的。

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。

而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。

也就是说,数据表中的一行记录,其实可能有多个版本(row),每个版本有自己的row trx_id。

如下图,就是一个记录被多个事务联系更新后的状态:

同一行数据4个版本

图中是同一行数据的4个版本,当前最新版本是V4,k的值是22,它是被transaction id 为 20 的事务更新的,因此它的row trx_id 也是20。

图中的虚箭头U1、U2、U3对应的是 undo log;V1、V2、V3并不是物理上真实存在的,而是每次需要的时候根据当前版本和 undo log 计算出来的。比如,需要V2版本的时候,就是通过V4 依次执行U3、U2算出来的。

按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。

如果一个数据版本是在启动之前生成的,可见;如果是在启动以后生成的,则不可见,需要按照数据版本往回找,直到可见为止。如果是自己更新的版本,则可见。

“快照”的实现

InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃”指的就是,启动了但还没有提交。

数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水位。

这个视图数组和高水位,就组成了当前事务的一致性视图(read-view),而数据版本的可见性规则,就是基于数据的 row trx_id 和 这个一致性视图的对比结果得到的。

这个视图数组把所有的row trx_id 分成了几种不同的情况。

  • begin/start transaction 启动事务
方式一启动事务

read_view 是在第一个select语句执行时生成的,而事务是在第一个操作InnoDB表时创建的。这就存在创建一致性读视图时,事务已经创建一段时间。此时视图数组为[103,104,105,106],低水位为102,高水位为106,当前事务为105。

  • start transaction with consistent snapshot 启动事务
方式二启动事务

read_view 和 事务是同步创建的,此时当前事务即为高水位。此时视图数组为[102,103,105],低水位为102,高水位为105,当前事务为105。

这样,对于当前事务来说,一个数据版本的row trx_id,有以下几种可能:

  1. 如果为绿色部分(肯定包括低于低水位事务),表示这个版本是已经提交的事务,这个数据是可见的
  2. 如果为红色部分(高于高水位事务),表示这个版本是由将来启动的事务生成的,不可见
  3. 如果为黄色部分,表示这个版本是未提交的事务,不可见

更新逻辑

可重复读的核心是“一致性读”,“一致性读” 针对的是读取操作,可以简单理解为 select。

但是,为了保证更新事务不丢失,更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。

读提交(RC) 和 可重复读(RR)的逻辑类似,他们最主要的区别是:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都公用这个一致性视图
  • 在读提交隔离级别下,每一个语句执行前都会重新计算出一个新的视图

小结

InnoDB的行数据有多个版本,每个数据版本有自己的row trx_id,每个事务或者语句有自己的一致性视图。普通查询语句是一致性读,一致性读会根据row trx_id 和 一致性视图确定数据版本的可见性。

  • 对于可重复读,查询只承认在事务启动前就已经提交完成的数据
  • 对于读提交,查询只承认在语句启动前就已经提交完成的数据
  • 当前读,总是读取已经提交完成的最新版本

相关文章

网友评论

      本文标题:【MySQL】8|事务到底是隔离的还是不隔离的

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