栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

事务是否需要隔离

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

事务是否需要隔离

文章目录
  • 1、前言
  • 2、快照在MVCC是如何工作的
  • 3、更新逻辑
    • 3.1、问题演变
    • 3.1、事务的可重复读是如何实现的

学习来源:mysql45讲

1、前言
  1. 事务的隔离级别为可重复读这个级别下,事务T启动的时候回创建一个视图read-view,之后事务T执行期间,即便其他事务修改了数据,事务T看到的仍然和启动的时候看到的一样。
  2. 但是在行锁的时候,如果两个事务要对同一行进行更新,那么第二个事务会被锁住,进入等待状态。那么既然进入等待状态,等到这个事务自己获取到行锁要更新数据的时候,读到的值又会是什么?
  3. 举例:
    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);
    
    1. 注:begin/start transaction命令并非是事务的起点,在执行到他们之后的第一个操作InnoDB表的语句,事务才真正启动。马上启动一个事务,命令是:start transaction with consistent snapshot。
    2. 第一种启动方式,一致性视图是在执行第一个快照语句的时候创建的。
    3. 第二种方式,一致性事务是在命令输入完成之后创建的。
  4. 在上面例子中:
    1. 事务C没有显示的使用begin/commit,表示这个update语句本身就是一个事务,语句完成的时候自动提交(因为autocommit=1)。
    2. 事务B在更新了行之后进行了查询。
    3. 事务A在一个只读的事务中查询,且时间顺序上是在事务B之后的。
  5. 答案:事务B查到的K值是3,事务A查到的K值是1。
  6. 在MySQL中有两个视图概念:(事务是没有物理结构的,作用是事务执行期间用来定义我能看见什么数据)
    1. 一个是view。这是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建的语法是create view (视图名) as …,查询方法与表一致。(常说的视图)
    2. 另一个是InnoDB在实现MVCC时用到的一致性视图,即:consistent read view,用于支持读已提交和可重复读这两个隔离级别的实现。(MVCC下的readaaaaview)
2、快照在MVCC是如何工作的
  1. 在可重复读的隔离级别下,事务在启动的时候就拍了一个快照,这个快照是基于整库的。
  2. 假设库有100G,启动一个事务,MySQL就把100G数据拷贝出来,过程太慢了。实际上,并并不需要拷贝出这100G的数据。
  3. InnoDB的每个事物都有一个事务ID,叫transaction id。是在事务开始的时候向InnoDB的事务系统申请的,按照申请顺序严格递增。
  4. 每行数据也是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把transaction id赋值给这个数据版本的事务ID,记row trx_id。同时,旧的版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。即:数据表中一行记录,其实是可能有多个版本的(row),每个版本都有自己的row trx_id。
  5. 一个记录被多个事务连续更新之后的状态。
    1. 虚线框中是同一行数据的4个版本,当前最新版本是V4,K的值为22。是被transaction id=25的事务更新的,所以他的row trx_id也是25。
    2. 语句更新会生成undo log(回滚日志),那么redo log在哪里?
    3. 实际上,上图中的三个虚线箭头就是undo log;V1、V2、V3并非物理上真实存在的,而是每次需要的时候根据当前版本和undo log计算出来的。比如,需要V2的时候,就是通过U3、U2算出来的。
    4. 个人觉得这里45讲中阐述的不是很生动形象,可以参考之前的博客MySQL事务隔离-MVCC

  6. InnoDB如何定义“100G”的快照?
  7. 可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果,但这之后,这个事务执行期间,其他事务的更新对它不可见。
  8. 因此,一个事务只需要在启动的时候声明说:以我启动的时刻为基准,如果一个数据版本是启动之前生成的,就认了;是启动之后才生成的,我不认,必须要找到它的上一个版本。
  9. 如果上一个版本还是不可见,那么就继续往前找。如果这个事务自己更新的数据,它自己还是要认的。
  10. 实际上,InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在活跃(启动了但还没提交)的所有事务Id。
  11. InnoDB利用了“所有数据都有多个版本”这个特性,实现了“秒级创建快照”的能力(依靠了MVCC,每次在进行快照读生成的readiview以及undolog版本链)
3、更新逻辑
  1. 那么上图事务B的update语句,如果按照readview,是否是结果不对的?(事务A查询出的值是1,事务B查询出的值是3)
    	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);
    


2. 事务B的视图数组是先生成出来的,之后事务C’才提交,那么是如何查询出3的?

3. 分析:

  1. 事务A先开启了一个一致性视图,查询的值必定是1,因为此时只是进行了快照读,随后commit。然后交出MDL读锁。
  2. 对于事务B来说,如果在update之前查询一次数据(快照读),那么这次查询出的值肯定也是1.
  3. 但是事务B此时的操作的是update(当前读),就不能再在之前的readview进行了,而在事务B更新之前,事务C’是进行了一个更新,那么事务B的更新就必须延续事务C’的结果,那么此时得到的readview是基于事务C’更新之后的,如果不基于事务C’的话,那么事务C’的更新就丢失了。所以,事务B此时的update是在(1,2)的基础上进行操作的。所以这就是为什么事务B读出来是3的原因。
  1. 什么是当前读?在之前的博客中有提到快照读和当前读,这里45讲给出了当前读的定义:更新数据都是先读后写的,而这个读,只能读当前的值。(还是记之前说的吧:insert、update、delete、select… for update(加写锁)、select … lock in share mode(加读锁))。
3.1、问题演变
  1. 如果此时事务C不是马上提交的呢?即:
  1. 此时事务C也开启了一个一致性视图,但是事务B先发起的一致性视图,在事务C进行update的时候,此时就重新生成了一个readview,即:事务C前后的readview是不一样的。
  2. 事务B呢?根据前面行锁的两阶段锁协议,因为事务C还没有提交,那么就在update同一行的时候加锁了,只有当事务C commit之后才能继续它的当前读。

3.1、事务的可重复读是如何实现的
  1. 可重复读的核心就是一致性读(consistent read);事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话那么就需要进入锁等待。
  2. “start transaction with consistent snapshot; ”的意思是从这个语句开始,创建一个持续整个事务的一致性快照。所以,在读提交隔离级别下,这个用法就没意义了,等效于普通的 start transaction。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/584488.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号