一文学会Mysql(三)Mysql事务

<h2>Mysql事务</h2><h4>1. ACID</h4><p>在关系型数据库管理系统中,一个逻辑工作单元要成为事务,必须满足这 4 个特性,即所谓的 ACID: 原子性(Atomicity)、一致性( Consistency)、隔离性( Isolation )和持久性( Durability)。</p><p><strong>1.1</strong> 原子性</p><p>原子性:事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。</p><p>修改---》 Buffer Pool修改---》刷盘。可能会有下面两种情况:</p><ol>
<li><p>事务提交了,如果此时Buffer Pool的脏页没有刷盘,这时候宕机了,如何保证修改的数据生效? Redo</p>
</li><li><p>如果事务没提交,但是Buffer Pool的脏页刷盘了,如何保证不该存在的数据撤销? Undo</p>
</li></ol><p>每一个写事务,都会修改BufferPool ,从而产生相应的Redo/Undo日志,在Buffer Pool 中的页被刷到 磁盘之前,这些日志信息都会先写入到日志文件中,如果 Buffer Pool 中的脏页没有刷成功,此时数据 库挂了,那在数据库再次启动之后,可以通过 Redo 日志将其恢复出来,以保证脏页写的数据不会丢失。如果脏页刷新成功,此时数据库挂了,就需要通过Undo来实现了。</p><p><strong>1.2</strong> 持久性</p><p>持久性:指的是一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,后续的操作或故障不 应该对其有任何影响,不会丢失。</p><p>如下图所示,一个“提交”动作触发的操作有: binlog落地、发送binlog、存储引擎提交、 flush_logs, check_point、事务提交标记等。这些都是数据库保证其数据完整性、持久性的手段。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-2d8616bcda7cfaf0.jpeg" img-data="{"format":"jpeg","size":9986,"height":160,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>MySQL的持久性也与WAL技术相关, redo log在系统Crash重启之类的情况时,可以修复数据,从而保 障事务的持久性。通过原子性可以保证逻辑上的持久性,通过存储引擎的数据刷盘可以保证物理上的持 久性。</p><p><strong>1.3</strong> 隔离性</p><p>隔离性:指的是一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并 发事务是隔离的。</p><p>InnoDB 支持的隔离性有 4 种,隔离性从低到高分别为:读未提交、读提交、可重复读、串行化。锁和多版本并发控制( MVCC )技术就是用于保障隔离性的(后面课程详解)。</p><p><strong>1.4</strong> 一致性</p><p>一致性:指的是事务开始之前和事务结束之后,数据库的完整性限制未被破坏。一致性包括两方面的内 容,分别是约束一致性和数据一致性。</p><p>约束一致性:创建表结构时所指定的外键、 Check、唯一索引等约束,可惜在 MySQL 中不支持 Check 。</p><p>数据一致性:是一个综合性的规定,因为它是由原子性、持久性、隔离性共同保证的结果,而不是 单单依赖于某一种技术。</p><p>一致性也可以理解为数据的完整性。数据的完整性是通过原子性、隔离性、持久性来保证的,而这3个 特性又是通过 Redo/Undo 来保证的。逻辑上的一致性,包括唯一索引、外键约束、 check 约束,这属 于业务逻辑范畴。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-e3d5c492bdeecdfe.jpeg" img-data="{"format":"jpeg","size":8875,"height":142,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>ACID 及它们之间的关系如下图所示, 4个特性中有3个与 WAL 有关系,都需要通过 Redo、 Undo 日志 来保证等。</p><p>WAL的全称为Write-Ahead Logging ,先写日志,再写磁盘。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-9a25a98b66df888e.jpeg" img-data="{"format":"jpeg","size":15460,"height":314,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><h4>2.并发事务控制</h4><p><strong>2.1</strong> 并发事务</p><p>事务并发处理可能会带来一些问题,比如:更新丢失、脏读、不可重复读、幻读等。</p><ol>
<li>更新丢失</li></ol><p>当两个或多个事务更新同一行记录,会产生更新丢失现象。可以分为回滚覆盖和提交覆盖。</p><ol>
<li><p>回滚覆盖:一个事务回滚操作,把其他事务已提交的数据给覆盖了。</p>
</li><li><p>提交覆盖:一个事务提交操作,把其他事务已提交的数据给覆盖了。</p>
</li></ol><p>脏读</p><p>一个事务读取到了另一个事务修改但未提交的数据。</p><p>不可重复读</p><p>一个事务中多次读取同一行记录不一致,后面读取的跟前面读取的不一致。</p><p>幻读</p><p>一个事务中多次按相同条件查询,结果数量不一致。后续查询的结果和面前查询结果不同,多了或少了几行记录。</p><p><strong>2.3</strong> 排队</p><p>最简单的方法,就是完全顺序执行所有事务的数据库操作,不需要加锁,简单的说就是全局排队。序列 化执行所有的事务单元,数据库某个时刻只处理一个事务操作,特点是强一致性,处理性能低。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-524ad727209b9a16.jpeg" img-data="{"format":"jpeg","size":7246,"height":166,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p><strong>2.2</strong> 排他锁</p><p>引入锁之后就可以支持并发处理事务,如果事务之间涉及到相同的数据项时,会使用排他锁,或叫互斥锁,先进入的事务独占数据项以后,其他事务被阻塞,等待前面的事务释放锁。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-bcc7fd77a041557a.jpeg" img-data="{"format":"jpeg","size":11040,"height":326,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>注意,在整个事务1结束之前,锁是不会被释放的,所以,事务2必须等到事务1结束之后开始。</p><p><strong>2.3</strong> 读写锁</p><p>读和写操作:读读、写写、读写、写读。</p><p>读写锁就是进一步细化锁的颗粒度,区分读操作和写操作,让读和读之间不加锁,这样下面的两个事务 就可以同时被执行了。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-bce6cd747ca48c00.jpeg" img-data="{"format":"jpeg","size":11302,"height":332,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>读写锁,可以让读和读并行,而读和写、写和读、写和写这几种之间还是要加排他锁。</p><h4>3.隔离级别</h4><p><strong>3.1</strong> 隔离级别类型</p><p>前面提到的“更新丢失”、”脏读”、“不可重复读”和“幻读”等并发事务问题,其实都是数据库一致性问题, 为了解决这些问题, MySQL数据库是通过事务隔离级别来解决的,数据库系统提供了以下 4 种事务隔 离级别供用户选择。</p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-1be15045beac144c.jpeg" img-data="{"format":"jpeg","size":14616,"height":153,"width":590}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><ol>
<li>读未提交</li></ol><p>Read Uncommitted 读未提交:解决了回滚覆盖类型的更新丢失,但可能发生脏读现象,也就是 可能读取到其他会话中未提交事务修改的数据。</p><ol>
<li>已提交读</li></ol><p>Read Committed 读已提交:只能读取到其他会话中已经提交的数据,解决了脏读。但可能发生 不可重复读现象,也就是可能在一个事务中两次查询结果不一致。</p><ol>
<li>可重复度</li></ol><p>Repeatable Read 可重复读:解决了不可重复读,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上会出现幻读,简单的说幻读指的的当用户读取某一范围的数 据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据时会发现有新的幻影行。</p><ol>
<li>可串行化</li></ol><p>Serializable 串行化:所有的增删改查串行执行。它通过强制事务排序,解决相互冲突,从而解决 幻度的问题。这个级别可能导致大量的超时现象的和锁竞争,效率低下。</p><p>数据库的事务隔离级别越高,并发问题就越小,但是并发处理能力越差(代价)。读未提交隔离级别最 低,并发问题多,但是并发处理能力好。以后使用时,可以根据系统特点来选择一个合适的隔离级别, 比如对不可重复读和幻读并不敏感,更多关心数据库并发处理能力,此时可以使用Read Commited隔 离级别。</p><p>事务隔离级别,针对Innodb引擎,支持事务的功能。像MyISAM引擎没有关系。</p><p>事务隔离级别和锁的关系</p><p>1 )事务隔离级别是SQL92定制的标准,相当于事务并发控制的整体解决方案,本质上是对锁和MVCC使 用的封装,隐藏了底层细节。</p><p>2)锁是数据库实现并发控制的基础,事务隔离性是采用锁来实现,对相应操作加不同的锁,就可以防 止其他事务同时对数据进行读写操作。</p><p>3 )对用户来讲,首先选择使用隔离级别,当选用的隔离级别不能解决并发问题或需求时,才有必要在 开发中手动的设置锁。</p><p>MySQL默认隔离级别:可重复读</p><p>Oracle、SQLServer默认隔离级别:读已提交</p><p>一般使用时,建议采用默认隔离级别,然后存在的一些并发问题,可以通过悲观锁、乐观锁等实现处 理。</p><p><strong>3.2</strong> <strong>MySQL</strong>隔离级别控制</p><p>MySQL默认的事务隔离级别是Repeatable Read ,查看MySQL当前数据库的事务隔离级别命令如下:</p><p>

show variables like 'tx_isolation';

select @@tx_isolation;

</p><p>设置事务隔离级别可以如下命令:</p><p>
set tx_isolation='READ-UNCOMMITTED';

set tx_isolation='READ-COMMITTED';

set tx_isolation='REPEATABLE-READ';

set tx_isolation='SERIALIZABLE';

</p><h4>4.MVCC(重点)</h4><h4>简介</h4><p>

什么是 MVCC ?
</p><p><strong>MVCC</strong>(Multi-Version Concurrency Control)即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。MVCC使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。</p><p><strong>MVCC</strong> 在 <strong>MySQL InnoDB</strong> 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,<strong>非阻塞并发读</strong></p><p>

什么是当前读和快照读?
</p><p>在学习 MVCC 多版本并发控制之前,我们必须先了解一下,什么是 MySQL InnoDB 下的当前读和快照读?</p><ol>
<li><p><strong>当前读</strong>
像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读。</p>
<p>为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁</p>
</li><li><p><strong>快照读</strong>
像不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的实现是基于多版本并发控制,而有可能是之前的历史版本。</p>
</li></ol><p><strong>MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是</strong> <strong>快照读</strong> <strong>, 而非当前读。</strong></p><p><strong>当前读实际上是一种加锁的操作,是悲观锁的实现</strong></p><p>

当前读,快照读和MVCC的关系
</p><ol>
<li>MVCC 多版本并发控制是 <strong>「维持一个数据的多个版本,使得读写操作没有冲突」</strong> 的概念,只是一个抽象概念,并非实现</li><li>因为 MVCC 只是一个抽象概念,要实现这么一个概念,MySQL 就需要提供具体的功能去实现它,<strong>「快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能」</strong>。而相对而言,当前读就是悲观锁的具体功能实现</li><li>要说的再细致一些,快照读本身也是一个抽象概念,再深入研究。MVCC 模型在 MySQL 中的具体实现则是由 <strong>3 个隐式字段</strong>,<strong>undo 日志</strong> ,<strong>Read View</strong> 等去完成的,具体可以看下面的 MVCC 实现原理</li></ol><p>
MVCC 能解决什么问题,好处是?
</p><p><strong>数据库并发场景有三种,分别为:</strong></p><ol>
<li>读-读:不存在任何问题,也不需要并发控制</li><li>读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读</li><li>写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失</li></ol><p><strong>MVCC 带来的好处是?</strong>
多版本并发控制(MVCC)是一种用来解决读-写冲突的<strong>无锁并发控制</strong>,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题</p><ol>
<li>在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能</li><li>同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题</li></ol><p><strong>如何使用MVCC解决并发问题?</strong>
有了 MVCC,所以我们可以形成两个组合:</p><ol>
<li>MVCC + 悲观锁
MVCC解决读写冲突,悲观锁解决写写冲突</li><li>MVCC + 乐观锁
MVCC 解决读写冲突,乐观锁解决写写冲突</li></ol><p>这种组合的方式就可以最大程度的提高数据库并发性能,并解决读写冲突,和写写冲突导致的问题</p><h4>MVCC 的实现原理</h4><p>MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 <strong>3个隐式字段</strong>,<strong>undo日志</strong> ,<strong>Read View</strong> 来实现的。所以我们先来看看这个三个 point 的概念</p><p>

<strong>隐式字段</strong>
</p><p>每行记录除了我们自定义的字段外,还有数据库隐式定义的 <strong>DB_TRX_ID, DB_ROLL_PTR, DB_ROW_ID</strong>等字段</p><ol>
<li>DB_TRX_ID
6 byte,最近修改(修改/插入)事务 ID:记录创建这条记录/最后一次修改该记录的事务 ID</li><li>DB_ROLL_PTR
7 byte,回滚指针,指向这条记录的上一个版本的undo log</li><li>DB_ROW_ID
6 byte,隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID产生一个聚簇索引</li></ol><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-7ef8d99bfede4ade.jpeg" img-data="{"format":"jpeg","size":13149,"height":206,"width":927}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
<strong>undo日志</strong>
</p><p>undo log 主要分为两种:</p><ol>
<li><strong>insert undo log</strong>
代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃</li><li><strong>update undo log</strong>
事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除</li></ol><p>对 MVCC 有帮助的实质是 update undo log ,undo log 实际上就是存在 rollback segment 中旧记录链,</p><p><strong>执行流程:</strong></p><p><strong>一、</strong> <strong>比如一个有个事务插入 persion 表插入了一条新记录</strong></p><p><strong>记录如下,</strong> <strong>name</strong> <strong>为 Jerry ,</strong> <strong>age</strong> <strong>为 24 岁,</strong> <strong>隐式主键</strong> <strong>是 1,</strong> <strong>事务 ID</strong> <strong>和</strong> <strong>回滚指针</strong> <strong>,我们假设为 NULL</strong></p><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-e1a582eb26c36076.jpeg" img-data="{"format":"jpeg","size":12359,"height":168,"width":833}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p><strong>二、</strong> <strong>现在来了一个</strong> <strong>事务 1</strong> <strong>对该记录的</strong> <strong>name</strong> <strong>做出了修改,改为 Tom</strong></p><ol>
<li>在事务 1修改该行(记录)数据时,数据库会先对该行加排他锁</li><li>然后把该行数据拷贝到 undo log 中,作为旧记录,既在 undo log 中有当前行的拷贝副本</li><li>拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务 ID 为当前事务 1的 ID, 我们默认从 1 开始,之后递增,回滚指针指向拷贝到 undo log 的副本记录,既表示我的上一个版本就是它</li><li>事务提交后,释放锁</li></ol><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-5e446e16b4282233.jpeg" img-data="{"format":"jpeg","size":33272,"height":361,"width":843}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p><strong>三、</strong> <strong>又来了个事务 2</strong> <strong>修改person 表</strong> 的同一个记录,将<strong>age</strong> <strong>修改为 30 岁</strong></p><ol>
<li>在事务2修改该行数据时,数据库也先为该行加锁</li><li>然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log 了,那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面</li><li>修改该行 age 为 30 岁,并且修改隐藏字段的事务 ID 为当前事务 2的 ID, 那就是 2 ,回滚指针指向刚刚拷贝到 undo log 的副本记录</li><li>事务提交,释放锁</li></ol><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-a8833f80630bc029.jpeg" img-data="{"format":"jpeg","size":48917,"height":513,"width":838}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>不同事务或者相同事务的对同一记录的修改,会导致该记录的<strong>undo log成为一条记录版本线链表</strong>,undo log 的链首就是最新的旧记录,链尾就是最早的旧记录</p><p>
<strong>Read View 读视图</strong>
</p><p><strong>什么是 Read View?</strong></p><p>Read View 就是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID</p><p>我们可以把 Read View 简单的理解成有三个全局属性</p><ol>
<li><p>活跃事务列表</p>
<ul>
<li>一个数值列表</li></ul>
<ul>
<li>用于维护 Read View 生成时刻系统 <strong>正活跃的事务 ID 列表</strong></li></ul>
</li><li><p>up_limit_id</p>
<ul>
<li>lower water mark</li></ul>
<ul>
<li><strong>是 trx_list 列表中事务 ID 最小的 ID</strong></li></ul>
</li><li><p>low_limit_id</p>
<ul>
<li>hight water mark</li></ul>
<ul>
<li>ReadView 生成时刻系统尚未分配的下一个事务 ID ,也就是 <strong>目前已出现过的事务 ID 的最大值 + 1</strong></li><li>为什么是 low_limit ? 因为它也是系统此刻可分配的事务 ID 的最小值</li></ul>
</li></ol><p>

</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-6931ed64af77a4d1.jpeg" img-data="{"format":"jpeg","size":23809,"height":438,"width":1014}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><p>这两个 ID 其实就可以从当前执行的事务的视角,将所有的事务分为三个部分</p><ol>
<li><p>小于低水位的部分一定是当前事务开始前就提交了的部分</p>
</li><li><p>大于等于高水位的则一定是还未提交的事务,我们一定不可见</p>
</li><li><p>处于中间的部分就要分类讨论了:</p>
<ul>
<li>如果在视图数组中,说明当前事务开始时,这些事务仍在活跃,所以应该是不可见的;</li><li>如果不在数组中,说明在仍活跃着的事务范围内,但其中有一些事务虽然不是开始最早的,但是结束的却比活跃数组中的事务早,以至于当前事务开始时,这些事务已经结束,所以就应该是可见的。</li></ul>
</li></ol><p>可见事务id总结</p><p><strong>要么比低水位更早,要么比高水位的 id 小但是不能出现在活跃事物数组中。</strong></p><p>

<strong>整体流程</strong>
</p><p><strong>流程模拟</strong></p><ol>
<li>当事务 2对某行数据执行了快照读,此时还有事务1和事务3在活跃中,事务 4在事务 2快照读前一刻提交更新了。</li></ol><p>

<strong>事务 1</strong>
<strong>事务 2</strong>
<strong>事务 3</strong>
<strong>事务 4</strong>

事务开始
事务开始
事务开始
事务开始




修改且已提交

进行中
快照读
进行中



</p><ol>
<li>up_limit_id 就是1,low_limit_id 就是 4 + 1 = 5,活跃事务列表值是 1, 3,Read View 如下图</li></ol><p>
</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-9c138f1801517258.jpeg" img-data="{"format":"jpeg","size":10202,"height":151,"width":696}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><ol>
<li>我们的例子中,只有事务 4 修改过该行记录,并在事务 2 执行快照读前,就提交了事务。所以当前DB_TRX_ID 为4</li></ol><p>
</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-7f174af3d4c6addb.jpeg" img-data="{"format":"jpeg","size":41034,"height":338,"width":761}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><ol>
<li><p><strong>拿该记录 DB_TRX_ID 字段记录的事务 ID 4 去跟 Read View 的 up_limit_id 比较</strong></p>
<ul>
<li><strong>DB_TRX_ID(4)大于 up_limit_id(1),所以不符合可见条件</strong></li><li><strong>继续判断 DB_TRX_ID(4) 是否大于等于 low_limit_id(5),不符和不可见条件</strong></li><li><strong>最后判断 DB_TRX_ID(4) 是否处于 trx_list 中的活跃事务, 最后发现事务 ID 为 4 的事务不在当前活跃事务列表中, 符合可见性条件</strong></li><li><strong>如果上述三点判断后不符合可见性,则去undolog链中找上一条记录的DB_TRX_ID,重复123步查找</strong></li></ul>
<p>所以事务 4修改后提交的最新结果对事务 2 快照读时是可见的,获取字段的值为'A'</p>
</li></ol><p>
</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/7061861-34e1085c02dbcfe3.jpeg" img-data="{"format":"jpeg","size":123778,"height":1141,"width":1647}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>
</p><h4>MVCC 相关问题</h4><p>
RR 是如何在 RC 级的基础上解决不可重复读的?
</p><p><strong>Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同</strong></p><p>
<strong>当前读和快照读在 RR 级别下的区别:</strong>
</p><p>表1:</p><p>

<strong>事务A</strong>
<strong>事务B</strong>

开启事务
开启事务

快照读(无影响)查询金额为500
快照读查询金额为500

更新金额为400

提交事务

select 快照读金额为500

select lock in share mode当前读金额为400

</p><p>在上表的顺序下,事务 B 的在事务 A 提交修改后的快照读是旧版本数据,而当前读是实时新数据 400</p><p>表2:</p><p>

<strong>事务A</strong>
<strong>事务B</strong>

开启事务
开启事务

快照读(无影响)查询金额为500

更新金额为400

提交事务

select 快照读金额为400

select lock in share mode当前读金额为400

</p><p>而在表 2这里的顺序中,事务 B 在事务 A 提交后的快照读和当前读都是实时的新数据 400,这是为什么呢?</p><p><strong>表2的第一次快照读在事务A更新金额为400而且commit之后发生</strong></p><p>

<strong>RC , RR 级别下的 InnoDB 快照读有什么不同?</strong>
</p><p>正是 Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同</p><p><strong>在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;</strong></p><p>

</p><p><strong>而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View。</strong></p>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容