事务到底是隔离的还是不隔离的 (具体)

这篇具有很好参考价值的文章主要介绍了事务到底是隔离的还是不隔离的 (具体)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

遇到不明白的慢慢往后读,一下你就明白了。

下面是一个只有两行的表的初始化语句

mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

事务到底是隔离的还是不隔离的 (具体),mysql,mysql 

         这里,我们需要注意的是事务的启动时机。

        begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作
InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令

        第一种启动方式,一致性视图是在第执行第一个快照读语句时创建的;
        第二种启动方式,一致性视图是在执行 start transaction with consistent
snapshot 时创建的

    我们这里都是默认autocommit=1。

        在这个例子中,事务 C 没有显式地使用 begin/commit,表示这个 update 语句本身就是
一个事务,语句完成的时候会自动提交。事务 B 在更新了行之后查询 ; 事务 A 在一个只读
事务中查询,并且时间顺序上是在事务 B 的查询之后。

        这时,如果我告诉你事务 B 查到的 k 的值是 3,而事务 A 查到的 k 的值是 1,你是不是感
觉有点晕呢?      

        在 MySQL 里,有两个“视图”的概念:

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

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

        在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库
的。
        这时,你会说这看上去不太现实啊。如果一个库有 100G,那么我启动一个事务,MySQL
就要拷贝 100G 的数据出来,这个过程得多慢啊。可是,我平时的事务执行起来很快啊

        实际上,我们并不需要拷贝出这 100G 的数据。我们先来看看这个快照是怎么实现的。

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

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

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

        如图 2 所示,就是一个记录被多个事务连续更新后的状态。

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

图 2 行状态变更图 

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

        语句更新会生成 undo log(回滚日志)吗?那么,undo log 在哪呢?

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

        明白了多版本和 row trx_id 的概念后,我们再来想一下,InnoDB 是怎么定义那
个“100G”的快照的。

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

        因此,一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本是
在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上
一个版本”。

        当然,如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新
的数据,它自己还是要认的.

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

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

        这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。

        而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到
的。

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

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

     图 3 数据版本可见性规则 

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

        1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数
据是可见的;

        2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;

        3. 如果落在黄色部分,那就包括两种情况

                 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;

                 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见 .

        比如,对于图 2 中的数据来说,如果有一个事务,它的低水位是 18,那么当它访问这一行
数据时,就会从 V4 通过 U3 计算出 V3,所以在它看来,这一行的值是 11.

        你看,有了这个声明后,系统里面随后发生的更新,是不是就跟这个事务看到的内容无关了
呢?因为之后的更新,生成的版本一定属于上面的 2 或者 3(a) 的情况,而对它来说,这些
新的数据版本是不存在的,所以这个事务的快照,就是“静态”的了。

        所以你现在知道了,InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒
级创建快照”的能力。

查询逻辑

        接下来,我们继续看一下图 1 中的三个事务,分析下事务 A 的语句返回的结果,为什么是
k=1。

这里,我们不妨做如下假设:

1. 事务 A 开始前,系统里面只有一个活跃事务 ID 是 99;
2. 事务 A、B、C 的版本号分别是 100、101、102,且当前系统里只有这四个事务;
3. 三个事务开始前,(1,1)这一行数据的 row trx_id 是 90。

这样,事务 A 的视图数组就是 [99,100], 事务 B 的视图数组是 [99,100,101], 事务 C 的视
图数组是 [99,100,101,102]。

为了简化分析,我先把其他干扰语句去掉,只画出跟事务 A 查询逻辑有关的操作:

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

图 4 事务 A 查询数据逻辑图

从图中可以看到,第一个有效更新是事务 C,把数据从 (1,1) 改成了 (1,2)。这时候,这个
数据的最新版本的 row trx_id 是 102,而 90 这个版本已经成为了历史版本。

第二个有效更新是事务 B,把数据从 (1,2) 改成了 (1,3)。这时候,这个数据的最新版本(即
row trx_id)是 101,而 102 又成为了历史版本。

你可能注意到了,在事务 A 查询的时候,其实事务 B 还没有提交,但是它生成的 (1,3) 这
个版本已经变成当前版本了。但这个版本对事务 A 必须是不可见的,否则就变成脏读了。

好,现在事务 A 要来读数据了,它的视图数组是 [99,100]。当然了,读数据都是从当前版
本读起的。所以,事务 A 查询语句的读数据流程是这样的:

找到 (1,3) 的时候,判断出 row trx_id=101,比高水位大,处于红色区域,不可见;
接着,找到上一个历史版本,一看 row trx_id=102,比高水位大,处于红色区域,不可
见;
再往前找,终于找到了(1,1),它的 row trx_id=90,比低水位小,处于绿色区域,可
见 

这样执行下来,虽然期间这一行数据被修改过,但是事务 A 不论在什么时候查询,看到这
行数据的结果都是一致的,所以我们称之为一致性读。

这个判断规则是从代码逻辑直接转译过来的,但是正如你所见,用于人肉分析可见性很麻
烦。

所以,我来给你翻译一下。一个数据版本,对于一个事务视图来说,除了自己的更新总是可
见以外,有三种情况:

1. 版本未提交,不可见;
2. 版本已提交,但是是在视图创建后提交的,不可见;
3. 版本已提交,而且是在视图创建前提交的,可见。

现在,我们用这个规则来判断图 4 中的查询结果,事务 A 的查询语句的视图数组是在事务
A 启动的时候生成的,这时候:

(1,3) 还没提交,属于情况 1,不可见;
(1,2) 虽然提交了,但是是在视图数组创建之后提交的,属于情况 2,不可见;
(1,1) 是在视图数组创建之前提交的,可见。

你看,去掉数字对比后,只用时间先后顺序来判断,分析起来是不是轻松多了。所以,后面
我们就都用这个规则来分析

更新逻辑

事务 B 的 update 语句,如果按照一致性读,好像结果不对哦? 

你看图 5 中,事务 B 的视图数组是先生成的,之后事务 C 才提交,不是应该看不见 (1,2)
吗,怎么能算出 (1,3) 来?

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

图 5 事务 B 更新逻辑图 

 是的,如果事务 B 在更新之前查询一次数据,这个查询返回的 k 的值确实是 1。

但是,当它要去更新数据的时候,就不能再在历史版本上更新了,否则事务 C 的更新就丢
失了。因此,事务 B 此时的 set k=k+1 是在(1,2)的基础上进行的操作。

所以,这里就用到了这样一条规则:更新数据都是先读后写的,而这个读,只能读当前的
值,称为“当前读”(current read)

因此,在更新的时候,当前读拿到的数据是 (1,2),更新后生成了新版本的数据 (1,3),这个
新版本的 row trx_id 是 101

所以,在执行事务 B 查询语句的时候,一看自己的版本号是 101,最新数据的版本号也是
101,是自己的更新,可以直接使用,所以查询得到的 k 的值是 3

这里我们提到了一个概念,叫作当前读。其实,除了 update 语句外,select 语句如果加
锁,也是当前读

所以,如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in
share mode 或 for update,也都可以读到版本号是 101 的数据,返回的 k 的值是 3。下
面这两个 select 语句,就是分别加了读锁(S 锁,共享锁)和写锁(X 锁,排他锁)。

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

再往前一步,假设事务 C 不是马上提交的,而是变成了下面的事务 C’,会怎么样呢?

事务到底是隔离的还是不隔离的 (具体),mysql,mysql 

图 6 事务 A、B、C'的执行流程

 

事务 C’的不同是,更新后并没有马上提交,在它提交前,事务 B 的更新语句先发起了。
前面说过了,虽然事务 C’还没提交,但是 (1,2) 这个版本也已经生成了,并且是当前的最
新版本。那么,事务 B 的更新语句会怎么处理呢? 

这时候,我们在上一篇文章中提到的“两阶段锁协议”就要上场了。事务 C’没提交,也就
是说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必须要读最新版本,而且必须
加锁,因此就被锁住了,必须等到事务 C’释放这个锁,才能继续它的当前读。

事务到底是隔离的还是不隔离的 (具体),mysql,mysql

图 7 事务 B 更新逻辑图(配合事务 C') 

到这里,我们把一致性读、当前读和行锁就串起来了。

现在,我们再回到文章开头的问题:事务的可重复读的能力是怎么实现的?

可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前
读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。文章来源地址https://www.toymoban.com/news/detail-626330.html

到了这里,关于事务到底是隔离的还是不隔离的 (具体)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL的事务隔离级别

    目录 事务隔离级别的概念 脏读(Dirty Read): 不可重复读(Non-Repeatable Read): 幻读(Phantom Read): 读未提交(Read Uncommitted) 读未提交隔离级别的特点 示例 优势和劣势 读已提交(Read Committed) 读已提交隔离级别的特点 示例 优势和劣势 可重复读(Repeatable Read) 可重复读隔

    2024年02月09日
    浏览(41)
  • 【MYSQL】事务隔离级别

    脏读 一个事务正在对一条记录做修改,在这个事务完成并提交前,另一个事务也来读取同一条记录,读取了这些未提交的“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫作’脏读’(Dirty Reads)。 例子:事务A修改了一条数据1状态

    2024年01月19日
    浏览(31)
  • Mysql 索引 、事务、隔离级别

    目录 索引(index) 1.为什么要有索引? 2.引入索引的代价 3.索引的操作 4.索引的使用场景 5.索引的底层原理 事务 (transaction) 事物的回滚是怎么做到的 事物的四大特性 并发执行事务带来的问题 隔离级别 索引是一种为了加快数据库查询(操作)速度而引入的一种手段,需要占用额

    2024年01月20日
    浏览(48)
  • 【MySQL】事务与隔离级别详解

    事务就是一组DML语句组成 ,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体(原子性)。例如买票操作,上层看来是一个单纯的买票操作,但是下层却需要一条或多条SQL语句来完成转账操作,这一组SQL是一个整体,被称为事务。事务还

    2024年02月15日
    浏览(35)
  • 网络安全--mysql中事务锁以及事务隔离解析

    专一性和历史性,例如一个男人历史上是花心的,但当他成长后开启begin和update后变的专一了,多项事务也影响不到他了,直到水泥封心,只可被一个人查询在此我们进入正题: 一、我们建立docker下的mysql镜像创建表格如下并且开启多会话进行实验:    第一事务:  第二事

    2024年02月13日
    浏览(31)
  • 【MySQL系列】MySQL的事务管理的学习(一)_ 事务概念 | 事务操作方式 | 事务隔离级别

    「前言」文章内容大致是MySQL事务管理。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 事务的概念 MySQL事务是指一系列的数据库操作(一组 DML 语句),这些操作要么全部成功执行,要么全部失败回滚。事务的目的是确保数据库的一致性和完整性 事务就是要

    2024年02月09日
    浏览(28)
  • 【MySQL】事务隔离机制 -- 必须说透

    如何控制 并发 是数据库领域中非常重要的问题之一,MySQL为了解决并发带来的问题,设计了事务隔离机制、锁机制、MVCC机制等,用一整套机制来解决并发问题,本文主要介绍事务隔离机制。 事务transaction(简写tx),在数据库中,事务是指一组逻辑操作,这些操作要么全部执

    2024年02月15日
    浏览(28)
  • mysql不同隔离级别事务插入数据

    面试中问到了相关问题,做下问题记录 问题是这样的,数据库默认隔离级别下,事务A插入一条数据,没有提交,事务B插入一条数据,提交,表id为自增,如果表是空表,事务A和事务B的id分别是多少。 2.1.1 打开一个mysql窗口,事务A 执行代码块 2.1.2 打开另一个窗口,事务B 执

    2024年02月11日
    浏览(39)
  • 【Mysql数据库 第13章】MySQL的事务、事务的隔离级别、事务的保存点

    💖Spring中的创建对象的三种方式、第三方资源配置管理详细描述及使用(XML版完结篇) 💖Spring中的bean的配置、作用范围、生命周期详细描述及使用(XML版上篇) 💖

    2023年04月20日
    浏览(48)
  • 【MySql】MySql事务隔离级别与一致性

    MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务方式进行 一个事务可能由多条SQL构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶段。而所谓的原子性,其实就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,可以

    2024年02月10日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包