1. 引例

之前我们探讨过可重复读隔离级别下,事务T启动的时候会创建一个视图read-view。在事务T执行期间,即使有其他事务修改了数据,事务T看到的也是跟启动时一样的。

但是上次讲到行锁的时候,当事务T要更新当前行的时候,其他事务占据了该行的行锁。那等
其他事务更新完,事务T要更新当前行的时候,看到的值又是多少呢?

首先,我们创建一个表,然后插入两行数据

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);

然后分别执行事务A、事务B、事务C

这里我们需要注意事务的启动时机,启动实际是存在两种方式的:

  1. begin/start transaction:这种命令的启动方式并不是立即启动事务,而是到执行第一个InnoDB的操作语句时,事务才真正启动。也就是说一致性视图是在第一个快照读语句时创建的
  2. start transaction with consistent snapshot:这种命令的启动方式能够立即启动事务。也就是说一致性视图是在执行该语句的时候就创建了

对于上图中,事务C没有显式地启动事务,但是执行完update语句就会立马提交。事务B在更新了行之后查询。事务A是一个只读地事务,且在事务B和事务C更新之后进行查询。

最终的答案是,事务A看到的值是1,事务B看到的值是3。

在MySQL中视图有两个含义:

  1. view:一个用查询语句定义的虚拟表,创建视图的语法是create view…
  2. consistent read view:InnoDB在实现MVCC时用到的一致性读视图,用于支持Read Committed(读提交)和Repeatable Read(可重复读)隔离级别的实现。

2. 快照在MVCC中是如何工作的?

在可重复读隔离级别下,事务在启动的时候就建立了一张快照,并且这个快照是基于整库的

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

而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新版本的数据,并且把transaction id赋值给这个数据版本的事务ID,记为row trx_id。也就是说,数据表中的一行数据,其实可能有多个版本,每个版本有自己的row trx_id。

如下图所示,就展现了一个记录被多个事务更新的状态。

V1、V2、V3并不是物理上实际存在的,而是每次需要的时候根据当前版本和redo log(回滚日志)计算出来的。比如,需要V2的时候,通过V4依次执行U3、U2算出来的。

接下来,我们来看快照是如何生成的?

根据可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。
也就是说,如果一个事务是在我启动之前生成的,就认。如果是我启动之后生成的,我就不认,必须要找到上一个可见的版本。此外,如果这个事务自己更新的数据,也是要认的

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

数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水位。这个视图数组和和高水位,就组成了当前事务的一致性视图

数据版本的可见性规则,就是基于数据的row trx_id和这个一致性视图对比结果得到的。如下图,分为以下几种情况:

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

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的。
  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的。
  3. 如果落在黄色部分,那就包含两种情况
    • 若row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    • 若row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

接着上面的说,对于第一个图中,如果有一个事务,它的低水位是18,那么当访问到这行数据的时候,就会从V4回滚到V3,在他看来,这一行的值是11。InnoDB利用了“所有数据都有多个版本”实现了秒级快照的能力。

然后,我们再回到最开始的引例,分析下为什么事务A看到的值是k=1。
我们先做如下的假设:

  1. 事务A开始前,系统分配的最大transaction 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查询语句读取数据的流程是这样的

  1. 找到(1,3)的时候,发现row trx_id=101,比高水位大,处于红色区域,不可见。
  2. 找到上一个历史版本(1,2),发现row trx_id=102,比高水位大,处于红色区域,不可见。
  3. 再找到上一个历史版本(1,1),发现row trx_id=90,比低水位小,处于绿色区域,可见。

但是上面这种判断方法太麻烦,我们可以简化判断方法

  • 前提:除了自己的更新总是可见之外
  • 版本未提交,不可见
  • 版本已提交,但是在视图创建后提交,不可见
  • 版本已提交,但是在视图创建前提交,可见。

我们再基于这种方法来判断事务A的查询流程:

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

3. 更新逻辑

可能你会发现,如果按照上面的逻辑,事务B不应该也看不见(1,2)吗?下面是事务B的事务逻辑图:

如果事务B只更新数据的话,那看到的确实是(1,1)。但是事务B先更新了数据,那么就不能在历史版本上更新了,否则就会出现丢失更新的情况。因此事务B的set k=k+1是在(1,2的基础上)完成的。

所以这里用到了一条规则:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。 因此,在执行事务B查询语句的时候,自己的版本号和最新的数据版本号都是101,所以查询得到k的值是3。

其实除了update语句外,select语句如果加锁,也是当前读。
因此,如果把事务A的查询语句,加上lock in share mode或for update,也都是返回k等于3。如下面这两个select语句,就是分别加了读锁和写锁。

mysql> select k from t where id=1 lock in share mode;
mysql> select k from t where id=1 for update;

再分析另外一种情况,如果事务C不是马上提交会怎么样?如下图:

因为事务C先将k修改为2,根据之前的两阶段协议,由于还没有提交,所以事务C会持有该行的行锁,直到事务提交才释放。又由于事务B是当前读,必须要读到最新版本,所以就需要等待事务C释放了锁才能修改值。如下图所示:

接下来,我们总结下事务的可重复读的能力是如何实现的
可重复读的核心就是一致性读;而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进行等待。

而对于读提交的逻辑其实也很像,主要区别在于?

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

那么在读提交的隔离级别下,事务A和事务B查询到的值是多少呢?

事务B的查询结果为k=3.但是由于事务B还没提交,事务C提交了,所以事务A的查询结果为k=2。

来源:自己整理的MySQL实战45讲笔记

事务到底是隔离还是不隔离?相关推荐

  1. Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别

    Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别 AOP:面向切面编程. 即在一个功能模块中新增其他功能,比方说你要下楼取个快递,你同事对你说 ...

  2. 一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 31.线程池复用的原理 32.spring是什么? 33.对Aop的理解 34.对IOC的理解 35.BeanFactor ...

  3. 数据库脏读、事务的四大特性、四大隔离级别、三大范式

    一.数据概念 1.脏数据所指的就是未提交的数据.也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数 ...

  4. mysql隔离性和线性隔离_MySQL--事务,隔离性和隔离级别

    事务 事务就是一组数据库操作,要么全部执行成功,要么全部执行失败,在MySQL中,事务是依靠存储引擎层实现的. ACID(Atomicity,Consistency,Isolation,Durabil ...

  5. 手机沙盒隔离软件_360隔离沙箱下载_360隔离沙箱独立版下载 v3.0.0 官方版_天天下载手机版...

    360隔离沙箱独立版是一款360安全卫士分离出来系统防护工具.360隔离沙箱独立版能帮助用户为自己电脑建立病毒隔离环境,在环境中运行程序就不用受到病毒侵袭,让用户办公娱乐更放心. 360隔离沙箱独立版 ...

  6. 关于以太网光纤收发器,逻辑隔离与物理隔离的理解与区别

    现如今,随着以太网的广泛应用,在很多领域,比如说电力.银行.公安.部队.铁路.大型企事业单位专网有广泛物理隔离的以太网接入需求,但是什么是物理隔离以太网呢?什么又是逻辑隔离以太网呢?我们该如何判断逻辑 ...

  7. DIN14 IPO系列 一路输入四路输出 模拟信号隔离分配器 光耦隔离

    主要特性: >>精度等级:0.1级.0.2级.产品出厂前已检验校正,用户可以直接使用 >>辅助电源:5V/12V/15V/24VDC(范围±10%) >>国际标准一 ...

  8. 信号隔离、电源隔离介绍

    修改时间:20221027 隔离 防止电击,保护处理器.AC或 FPGA 免受高压损坏风险,中断接地回路和通信网络: 隔离原因 确保安全. 解决接地电位差 提高电路抗噪能力. 两种隔离方法: 模拟.数 ...

  9. 什么是物理隔离?物理隔离光端机是什么?

    什么是物理隔离? 物理隔离,是指采用物理方法将内网与外网隔离从而避免入侵或信息泄露的风险的技术手段.物理隔离主要用来解决网络安全问题的,尤其是在那些需要绝对保证安全的保密网,专网和特种网络与互联网进行 ...

  10. 08 | 事务到底是隔离的还是不隔离的

      如果是可重复读隔离级别,事务 T 启动的时候会创建一个视图 read-view,之后事务 T 执行期间,即使有其他事务修改了数据,事务 T 看到的仍然跟在启动时看到的一样.也就是说,一个在可重复读 ...

最新文章

  1. 使用TextInputLayout分分钟构造一个酷炫登录框架
  2. 2021春季学期-创新设计与实践-Lesson5
  3. 使用asp.net MVC4中的Bundle遇到的问题及解决办法
  4. 翻译 | ORB: An efficient alternative to SIFT or SURF(ORB:对SIFT或SURF的一种有效选择)
  5. 【2016年第5期】位置大数据在车辆保险风险管理中的应用
  6. 振型矩阵与正则振型矩阵
  7. python编程规则_python编程规则
  8. 太长的sql怎么分析_因为ESR, 我一定要推荐你这款 SQL 神器
  9. redis数据库操作(3)
  10. HDU 5071 模拟
  11. H5JS二维动画制作!two.js的基本操作class2
  12. 凯撒密码 (Python实现)
  13. 算法的时间复杂度和空间复杂度详解
  14. 数据结构设计题大题总结(非代码)
  15. linux下的文件系统,Linux系统中常见的文件系统有哪些?
  16. 读书之二 --《程序员修炼之道》
  17. BeagleBone Black– 智能家居控制系统 LAS - ESP8266 UDP 服务
  18. java正则表达式匹配单引号_java - 正则表达式,用于在未被单引号或双引号括起时使用空格分割字符串...
  19. Easy Excel生成压缩包文件,自定义表头样式
  20. 怎么在计算机里找到CF里保存的视频,Win10电脑上查看穿越火线录制保存视频的具体方法...

热门文章

  1. 我们的法兰西岁月-2
  2. 无视法律:利用勒索软件攻击巴西最高法院
  3. MySQL 主从同步percona-toolkit工具(数据一致性监测、延迟监控)使用梳理
  4. 安卓 App 性能专项测试之流畅度深度解析-上篇
  5. PPT报告的一些“技巧”
  6. 单元测试JUnit 4
  7. Windows API 获取卷的基本信息
  8. python数据分许基础
  9. 【安卓学习之第三方库】 身份证、银行卡、营业执照OCR识别
  10. 弘辽科技:淘宝搜索权重上不去怎么办?什么是搜索权重?