范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

一文通过MySQL解读4种事务隔离级别的实现

  说明:本文在小林作者的基础上做了修订,增加并发一致性丢失的问题场景
  这是我的钱包,共有 100 万元。
  今天我心情好,我决定给你的转账 100 万,最后的结果肯定是我的余额变为 0 元,你的余额多了 100 万元,是不是想到就很开心?
  转账这一动作在程序里会涉及到一系列的操作,假设我向你转账 100 万的过程是有下面这几个步骤组成的:
  可以看到这个转账的过程涉及到了两次修改数据库的操作。
  假设在执行第三步骤之后,服务器忽然掉电了,就会发生一个蛋疼的事情,我的账户扣了 100 万,但是钱并没有到你的账户上,也就是说  这 100 万消失了!
  要解决这个问题,就要保证转账业务里的所有数据库的操作是不可分割的,要么全部执行成功 ,要么全部失败,不允许出现中间状态的数据。
  数据库中的「  事务(  Transaction  ) 」就能达到这样的效果。
  我们在转账操作前先开启事务,等所有数据库操作执行完成后,才提交事务,对于已经提交的事务来说,该事务对数据库所做的修改将永久生效,如果中途发生发生中断或错误,那么该事务期间对数据库所做的修改将会被回滚到没执行该事务之前的状态。
  没错,今天就来图解 MySQL 事务啦。
  事务有哪些特性?
  事务是由 MySQL 的引擎来实现的,我们常见的 InnoDB 引擎它是支持事务的。
  不过并不是所有的引擎都能支持事务,比如 MySQL 原生的 MyISAM 引擎就不支持事务,也正是这样,所以大多数 MySQL 的引擎都是用 InnoDB。
  事务看起来感觉简单,但是要实现事务必须要遵守 4 个特性,分别如下:原子性(Atomicity) :一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。一致性(Consistency) :是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。隔离性(Isolation) :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。持久性(Durability) :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?持久性是通过 redo log (重做日志)来保证的;原子性是通过 undo log(回滚日志) 来保证的;隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;一致性则是通过持久性+原子性+隔离性来保证;
  这次将重点介绍事务的隔离性 ,这也是面试时最常问的知识的点。
  为什么事务要有隔离性,我们就要知道并发事务时会引发什么问题。并行事务会引发什么问题?
  MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。
  那么在同时处理多个事务的时候,就可能出现丢失更新(Last to Modify)、脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题 。
  接下来,通过举例子给大家说明,这些问题是如何发生的。
  # 丢失更新 Last To Modify
  丢失更新非常好理解,简单来说其就是 一个事务的更新操作会被另一个事务的更新操作所覆盖 ,从而导致数据的不一致。
  举个例子:
  1)事务 T1 将行记录 r 更新为 v1,但是事务 T1 并未提交
  2)与此同时,事务 T2 将行记录 r 更新为 v2,事务 T2 未提交
  3)事务 T1 提交
  4)事务 T2 提交
  如下图所示,显然,事务 T1 丢失了自己的修改。
  但是,事实上,这种情况准确来讲并不会发生。
  因为我们说过对于行进行更新操作的时候,需要对行或其他粗粒度级别的对象加锁,因此当事务 T1 修改行 r 但是没提交的时候,事务 T2 对行 r 进行更新操作的时候是会被阻塞住的,直到事务 T1 提交释放锁。
  所以, 从数据库层面来讲,数据库本身是可以帮助我们阻止丢失更新问题的发生的 。
  不过,在真实的开发环境中,我们还经常会遇到 逻辑意义上的丢失更新 。举个例子:
  1)事务 T1 查询一行数据 r,放入本地内存,并显示给一个用户 User1
  2)事务 T2 也查询该行数据,并将取得的数据显示给另一个用户 User2
  3)User1 修改了行记录 r 为 v1,更新数据库并提交
  4)User2 修改了行记录 r 为 v2,更新数据库并提交
  显然,最终这行记录的值是 v2,User1 的更新操作被 User2 覆盖掉了,丢失了他的修改。
  可能还是云里雾里,我来举个 更现实点的例子 吧,一个部门共同查看一个在线文档,员工 A 发现自己的性别信息有误,于是将其从 "女" 改成了 "男",就在这时,HR 也发现了员工 A 的部门信息有误,于是将其从 "测试" 改成了 "开发",然后,员工 A 和 HR 同时点了提交,但是 HR 的网络稍微慢一点,再次刷新,员工 A 就会发现,我的性别怎么还是 "女"?
  #  脏读
  如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。
  举个栗子。
  假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库中读取小林的余额数据,然后再执行更新操作,如果此时事务 A 还没有提交事务,而此时正好事务 B 也从数据库中读取小林的余额数据,那么事务 B 读取到的余额数据是刚才事务 A 更新后的数据,即使没有提交事务。
  因为事务 A 是还没提交事务的,也就是它随时可能发生回滚操作,如果在上面这种情况事务 A 发生了回滚,那么事务 B 刚才得到的数据就是过期的数据,这种现象就被称为脏读。
  #  不可重复读
  在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。
  举个栗子。
  假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库中读取小林的余额数据,然后继续执行代码逻辑处理,在这过程中如果事务 B 更新了这条数据,并提交了事务,那么当事务 A 再次读取该数据时,就会发现前后两次读到的数据是不一致的,这种现象就被称为不可重复读。
  #  幻读
  在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。
  举个栗子。
  假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库查询账户余额大于 100 万的记录,发现共有 5 条,然后事务 B 也按相同的搜索条件也是查询出了 5 条记录。
  接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事务,此时数据库超过 100 万余额的账号个数就变为 6。
  然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,发现和前一次读到的记录数量不一样了,就感觉发生了幻觉一样,这种现象就被称为幻读。 事务的隔离级别有哪些?
  前面我们提到,当多个事务并发执行时可能会遇到「脏读、不可重复读、幻读」的现象,这些现象会对事务的一致性产生不同程序的影响。脏读:读到其他事务未提交的数据;不可重复读:前后读取的数据不一致;幻读:前后读取的记录数量不一致。
  这三个现象的严重性排序如下:
  SQL 标准提出了四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低,这四个隔离级别如下:读未提交( read uncommitted ),指一个事务还没提交时,它做的变更就能被其他事务看到;读提交( read committed ),指一个事务提交之后,它做的变更才能被其他事务看到;可重复读( repeatable read ),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别 ;串行化( serializable  );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;
  按隔离水平高低排序如下:
  针对不同的隔离级别,并发事务时可能发生的现象也会不同。
  也就是说:在「读未提交」隔离级别下,可能发生脏读、不可重复读和幻读现象;在「读提交」隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;在「可重复读」隔离级别下,可能发生幻读现象,但是不可能脏读和不可重复读现象;在「串行化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。
  所以,要解决脏读现象,就要升级到「读提交」以上的隔离级别;要解决不可重复读现象,就要升级到「可重复读」的隔离级别,要解决幻读现象不建议将隔离级别升级到「串行化」。
  不同的数据库厂商对 SQL 标准中规定的 4 种隔离级别的支持不一样,有的数据库只实现了其中几种隔离级别,我们讨论的 MySQL 虽然支持 4 种隔离级别,但是与SQL 标准中规定的各级隔离级别允许发生的现象却有些出入 。
  MySQL 在「可重复读」隔离级别下,可以很大程度上避免幻读现象的发生(注意是很大程度避免,并不是彻底避免),所以 MySQL 并不会使用「串行化」隔离级别来避免幻读现象的发生,因为使用「串行化」隔离级别会影响性能。
  MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了,详见这篇 文章 (opens new window) ),解决的方案有两种:针对快照读 (普通 select 语句),是通过 MVCC 方式解决了幻读 ,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。针对当前读 (select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读 ,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。
  接下来,举个具体的例子来说明这四种隔离级别,有一张账户余额表,里面有一条账户余额为 100 万的记录。然后有两个并发的事务,事务 A 只负责查询余额,事务 B 则会将我的余额改成 200 万,下面是按照时间顺序执行两个事务的行为:
  在不同隔离级别下,事务 A 执行过程中查询到的余额可能会不同:在「读未提交」隔离级别下,事务 B 修改余额后,虽然没有提交事务,但是此时的余额已经可以被事务 A 看见了,于是事务 A 中余额 V1 查询的值是 200 万,余额 V2、V3 自然也是 200 万了;在「读提交」隔离级别下,事务 B 修改余额后,因为没有提交事务,所以事务 A 中余额 V1 的值还是 100 万,等事务 B 提交完后,最新的余额数据才能被事务 A 看见,因此额 V2、V3 都是 200 万;在「可重复读」隔离级别下,事务 A 只能看见启动事务时的数据,所以余额 V1、余额 V2 的值都是 100 万,当事务 A 提交事务后,就能看见最新的余额数据了,所以余额 V3 的值是 200 万;在「串行化」隔离级别下,事务 B 在执行将余额 100 万修改为 200 万时,由于此前事务 A 执行了读操作,这样就发生了读写冲突,于是就会被锁住,直到事务 A 提交后,事务 B 才可以继续执行,所以从 A 的角度看,余额 V1、V2 的值是 100 万,余额 V3 的值是 200万。
  这四种隔离级别具体是如何实现的呢?对于「读未提交」隔离级别的事务来说,因为可以读到未提交事务修改的数据,所以直接读取最新的数据就好了;对于「串行化」隔离级别的事务来说,通过加读写锁的方式来避免并行访问;对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同,大家可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View,而「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View 。
  注意,执行「开始事务」命令,并不意味着启动了事务。在 MySQL 有两种开启事务的命令,分别是:第一种:begin/start transaction 命令;第二种:start transaction with consistent snapshot 命令;
  这两种开启事务的命令,事务的启动时机是不同的:执行了 begin/start transaction 命令后,并不代表事务启动了。只有在执行这个命令后,执行了增删查改操作的 SQL 语句,才是事务真正启动的时机;执行了 start transaction with consistent snapshot 命令,就会马上启动事务。
  接下来详细说下,Read View 在 MVCC 里如何工作的?Read View 在 MVCC 里如何工作的?
  我们需要了解两个知识:Read View 中四个字段作用;聚簇索引记录中两个跟事务有关的隐藏列;
  那 Read View 到底是个什么东西?
  Read View 有四个重要的字段:m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表 ,注意是一个列表,"活跃事务"指的就是,启动了但还没提交的事务 。min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务 ,也就是 m_ids 的最小值。max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值 ,也就是全局事务中最大的事务 id 值 + 1;creator_trx_id :指的是创建该 Read View 的事务的事务 id 。
  知道了 Read View 的字段,我们还需要了解聚簇索引记录中的两个隐藏列。
  假设在账户余额表插入一条小林余额为 100 万的记录,然后我把这两个隐藏列也画出来,该记录的整个示意图如下:
  对于使用 InnoDB 存储引擎的数据库表,它的聚簇索引记录中都包含下面两个隐藏列:trx_id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务的事务 id 记录在 trx_id 隐藏列里 ;roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后这个隐藏列是个指针,指向每一个旧版本记录 ,于是就可以通过它找到修改前的记录。
  在创建 Read View 后,我们可以将记录中的 trx_id 划分这三种情况:
  一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:如果记录的 trx_id 值小于 Read View 中的 min_trx_id   值,表示这个版本的记录是在创建 Read View 前 已经提交的事务生成的,所以该版本的记录对当前事务可见 。如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id   值,表示这个版本的记录是在创建 Read View 后 才启动的事务生成的,所以该版本的记录对当前事务不可见 。如果记录的 trx_id 值在 Read View 的 min_trx_id   和 max_trx_id   之间,需要判断 trx_id 是否在 m_ids 列表中:如果记录的 trx_id 在  m_ids   列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见 。如果记录的 trx_id 不在  m_ids  列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见 。
  这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)。 可重复读是如何工作的?
  可重复读隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View 。
  假设事务 A (事务 id 为51)启动后,紧接着事务 B (事务 id 为52)也启动了,那这两个事务创建的 Read View 如下:
  事务 A 和 事务 B 的 Read View 具体内容如下:在事务 A 的 Read View 中,它的事务 id 是 51,由于它是第一个启动的事务,所以此时活跃事务的事务 id 列表就只有 51,活跃事务的事务 id 列表中最小的事务 id 是事务 A 本身,下一个事务 id 则是 52。在事务 B 的 Read View 中,它的事务 id 是 52,由于事务 A 是活跃的,所以此时活跃事务的事务 id 列表是 51 和 52,活跃的事务 id 中最小的事务 id 是事务 A ,下一个事务 id 应该是 53。
  接着,在可重复读隔离级别下,事务 A 和事务 B 按顺序执行了以下操作:事务 B 读取小林的账户余额记录,读到余额是 100 万;事务 A 将小林的账户余额记录修改成 200 万,并没有提交事务;事务 B 读取小林的账户余额记录,读到余额还是 100 万;事务 A 提交事务;事务 B 读取小林的账户余额记录,读到余额依然还是 100 万;
  接下来,跟大家具体分析下。
  事务 B 第一次读小林的账户余额记录,在找到记录后,它会先看这条记录的 trx_id,此时发现 trx_id 为 50,比事务 B 的 Read View 中的 min_trx_id 值(51)还小,这意味着修改这条记录的事务早就在事务 B 启动前提交过了,所以该版本的记录对事务 B 可见的 ,也就是事务 B 可以获取到这条记录。
  接着,事务 A 通过 update 语句将这条记录修改了(还未提交事务),将小林的余额改成 200 万,这时 MySQL 会记录相应的 undo log,并以链表的方式串联起来,形成版本链 ,如下图:
  你可以在上图的「记录的字段」看到,由于事务 A 修改了该记录,以前的记录就变成旧版本记录了,于是最新记录和旧版本记录通过链表的方式串起来,而且最新记录的 trx_id 是事务 A 的事务 id(trx_id = 51)。
  然后事务 B 第二次去读取该记录,发现这条记录的 trx_id 值为 51,在事务 B 的 Read View 的 min_trx_id 和 max_trx_id 之间,则需要判断 trx_id 值是否在 m_ids 范围内,判断的结果是在的,那么说明这条记录是被还未提交的事务修改的,这时事务 B 并不会读取这个版本的记录。而是沿着 undo log 链条往下找旧版本的记录,直到找到 trx_id 「小于」事务 B 的 Read View 中的 min_trx_id 值的第一条记录 ,所以事务 B 能读取到的是 trx_id 为 50 的记录,也就是小林余额是 100 万的这条记录。
  最后,当事物 A 提交事务后,由于隔离级别时「可重复读」,所以事务 B 再次读取记录时,还是基于启动事务时创建的 Read View 来判断当前版本的记录是否可见。所以,即使事物 A 将小林余额修改为 200 万并提交了事务, 事务 B 第三次读取记录时,读到的记录都是小林余额是 100 万的这条记录 。
  就是通过这样的方式实现了,「可重复读」隔离级别下在事务期间读到的记录都是事务启动前的记录。读提交是如何工作的?
  读提交隔离级别是在每次读取数据时,都会生成一个新的 Read View 。
  也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
  那读提交隔离级别是怎么工作呢?我们还是以前面的例子来聊聊。
  假设事务 A (事务 id 为51)启动后,紧接着事务 B (事务 id 为52)也启动了,接着按顺序执行了以下操作:事务 B 读取数据(创建 Read View),小林的账户余额为 100 万;事务 A 修改数据(还没提交事务),将小林的账户余额从 100 万修改成了 200 万;事务 B 读取数据(创建 Read View),小林的账户余额为 100 万;事务 A 提交事务;事务 B 读取数据(创建 Read View),小林的账户余额为 200 万;
  那具体怎么做到的呢?我们重点看事务 B 每次读取数据时创建的 Read View。前两次 事务 B 读取数据时创建的 Read View 如下图:
  我们来分析下为什么事务 B 第二次读数据时,读不到事务 A (还未提交事务)修改的数据?
  事务 B 在找到小林这条记录时,会看这条记录的 trx_id 是 51,在事务 B 的 Read View 的 min_trx_id 和 max_trx_id 之间,接下来需要判断 trx_id 值是否在 m_ids 范围内,判断的结果是在的,那么说明这条记录是被还未提交的事务修改的,这时事务 B 并不会读取这个版本的记录 。而是,沿着 undo log 链条往下找旧版本的记录,直到找到 trx_id 「小于」事务 B 的 Read View 中的 min_trx_id 值的第一条记录,所以事务 B 能读取到的是 trx_id 为 50 的记录,也就是小林余额是 100 万的这条记录。
  我们来分析下为什么事务 A 提交后,事务 B 就可以读到事务 A 修改的数据?
  在事务 A 提交后,由于隔离级别是「读提交」,所以事务 B 在每次读数据的时候,会重新创建 Read View ,此时事务 B 第三次读取数据时创建的 Read View 如下:
  事务 B 在找到小林这条记录时,会发现这条记录的 trx_id 是 51,比事务 B 的 Read View 中的 min_trx_id 值(52)还小,这意味着修改这条记录的事务早就在创建 Read View 前提交过了,所以该版本的记录对事务 B 是可见的 。
  正是因为在读提交隔离级别下,事务每次读数据时都重新创建 Read View,那么在事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。总结
  事务是在 MySQL 引擎层实现的,我们常见的 InnoDB 引擎是支持事务的,事务的四大特性是原子性、一致性、隔离性、持久性,我们这次主要讲的是隔离性。
  当多个事务并发执行的时候,会引发脏读、不可重复读、幻读这些问题,那为了避免这些问题,SQL 提出了四种隔离级别,分别是读未提交、读已提交、可重复读、串行化,从左往右隔离级别顺序递增,隔离级别越高,意味着性能越差,InnoDB 引擎的默认隔离级别是可重复读。
  要解决脏读现象,就要将隔离级别升级到读已提交以上的隔离级别,要解决不可重复读现象,就要将隔离级别升级到可重复读以上的隔离级别。
  而对于幻读现象,不建议将隔离级别升级为串行化,因为这会导致数据库并发时性能很差。MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了,详见这篇文章 (opens new window) ),解决的方案有两种:针对快照读 (普通 select 语句),是通过 MVCC 方式解决了幻读 ,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。针对当前读 (select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读 ,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。
  对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同:「读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。
  这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列」的比对,来控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。
  在可重复读隔离级别中,普通的 select 语句就是基于 MVCC 实现的快照读,也就是不会加锁的。而 select .. for update 语句就不是快照读了,而是当前读了,也就是每次读都是拿到最新版本的数据,但是它会对读到的记录加上 next-key lock 锁。

SpaceX成功发射日本HakutoR月球登陆装置在夜空中创造出美妙图案SpaceX今天清晨进行了一次月球方向的发射,此前它从佛罗里达州的卡纳维拉尔角空军基地发射了日本公司ispaceInc的月球登陆器,这次发射是SpaceX今年的第二次月球方向的发射小米13你好,iPhone14再见我也没想过,我居然会从618看到双十二,手机还是没有定下来买哪台之前一度下单买了iPhone14,但是发现它和我的iPhone12几乎是一样的,同一样的刘海屏,同一样的20W慢充,OPPO首个健康概念产品官宣,是啥很神秘!OPPOFindN2Flip副屏绝了12月14日,OPPO未来科技大会2022就要召开了,根据官方给出的信息,此次无疑是一场OPPO秀技术肌肉的大会,因为OPPO第二颗自研芯片将会到来。据悉,OPPO第二颗自研芯片与一只绰号为雷神的海象在英国一处海滩安家,当地人已被警告远离它一只绰号为雷神的海象把英国的一个海滩当成了自己的家,但当地人被警告要远离它。这只海象是10号早上被发现的。发布在社交媒体上的录像显示,这只重达两吨的海象在爬到沙滩上后,悠闲地躺在那截至目前,盘点搭载索尼IMX989一英寸传感器的手机1小米12SUltra全球首发第一代骁龙8处理器,采用台积电4nm制程工艺,性能更强,功耗表现也得到大幅度的改善,体验也跃升新高正面是一块6。73英寸的三星E5发光材料的AMOLE我国农机装备总量近2亿台(套)农作物耕种收综合机械化率超72央视网消息一年四季,不管什么样的地形,也无论是小麦水稻,还是玉米大豆,都有自己的专属农机。而这背后,正是我国不断升级的农机装备制造能力。眼下,冬小麦的播种已基本结束。在华北地下水漏一位少年英雄救下10万红军,毛泽东发现后果断下令必须找到他!1934年9月,蒋介石在庐山牯岭秘密召开军事会议,会议只有一个目标,那就是对红军进行第五次围剿。蒋介石想通过这次铁桶围剿彻底摧毁中央苏区。在会议结束后,这份绝密文件自然需要交给蒋介为什么不少人会把宝宝的胎毛保留起来?保留胎毛有什么作用?2022育儿季导读胎毛,也就是宝宝与生俱来的毛发,是从胎儿时期就生长开来的,也算是头发最初的那一部分。所以说,给自己的小宝宝剪胎发是一件非常有仪式感和纪念意义的事情,很多父母就会将78岁男星独居在家中浴室去世!隔天才被亲人发现,曾与王祖贤合作12月12日,据日本媒体报道称,日本知名男演员佐藤蛾次郎在家中不幸离世,终年78岁,这段时间去世的中外明星好像有不少,而该明星去世的消息也是引来无数网友的热议。据悉,佐藤蛾次郎是在十三塑造地球表面景观水文循环和崩坏作用2(崩坏作用)4。崩坏作用4。1什么是崩坏作用1970年5月31日,秘鲁安第斯小镇Yungay发生地震。地面震动还打破了一块800米宽的冰板,这块冰从NevadoHuascaran山顶的冰川末端政策效能持续释放助力我国外贸稳增长央视网消息今年以来,从国家到地方积极出台了多项举措,促进跨境电商海外仓等外贸新业态新模式创新发展,政策效能的持续释放也为我国外贸高质量发展提供了源源不断的新动能。海外仓是新型外贸基
海尔,格力,美的三家公司体量市场占有率都差不多,为什么海尔和其他两家市值有了差距?海尔是中国家电老大很多年了,但作为海尔集团掌舵人的张瑞敏并没有产生歇一歇的念头。相反,在并未遭遇巨大困境的背景下,张瑞敏主动发起一场变革,将海尔既有经营模式与组织结构打碎,架构一个电子脚镣技术这样黑科技有多强呢?为什么不能自己去掉呢?感谢提问探讨,关于电子脚镣也不算黑科技了,原理就是通过一个小型放电装置(就是电警棍原理)来约束被配戴人员的行动,触发开关有两种方式1。遥控触发。2。距离触发,超出控制器范围会自动放人常吃牛羊肉,为什么却独不吃马肉?市场上的马肉都到哪去了?人常吃牛羊肉,为什么却独不吃马肉?1。马曾作为战略物资和运输工具,自古受控,不可随意宰杀。在古代,马是人类生活和作战的工具,战士可以骑马作为骑兵对步兵形成优势,可以千里奔袭,可以运5年不见,中国高端市场已成红海,酷派COOL20Pro还有机会吗?难说。2年前国内手机市场海就已经不那么蓝了,华为如日中天,全球手机市场份额稳居第二,如此环境下,iqoo和realme这样的新品牌都能打出一片天地,摩托酷派等老品牌出山也未必就杀不少林寺和少林有不同吗?少林寺和少林是有区别的,少林,是天下少林弟子或者师从于少林寺弟子的人的统称,也就是少林派,电影少林小子就是讲的从少林寺还俗的两兄弟和他们收养的八个孤儿,就是自称少林弟子,三龙的笨弟吸油烟机十大品牌谁了解?有方太,老板,帅康,华帝,西门子,美的,长城,樱花,万和,其中长城厨卫是一个非常不错的厨卫品牌,已经有很多人都推荐了,他们家是专业做厨电产品的,而且品种也很多,每个产品在业内都超级什么品牌的路由器耐用又稳定?选择路由器关键看三点有线速率无线速率带机量。同样是千兆无线路由器,不同品牌不同价位差距很大,就像汽车一样,同样是四个轮子,BBA的要贵很多。有线速率有线速率是指有线端口WAN端口L上大学要不要转户口呢?一句话,北上广你就转,非北上广你就不要转。我们先看看关于大学转户口的流程。大学入学时,是否选择转户口实行自愿选择,愿意转入的也只是在学校的集体户,本质上是临时户口的意思。1大学期间健身可以改变体态吗?健身当然可以改变体态,而且,健身改变的不仅仅是体态!但是,这是有前提条件的,这个条件就是坚持坚持正确的坚持正确的科学的健身运动!为什么这样说呢?一首先说健身为什么可以改变体态。健身如果在戒烟过程中实在忍不住了,应该怎么办?忍住!再过1天就成功了。1天!使劲夸夸自已,一天也是胜利!对自已说出来老子吸的是烟又不是毒,能戒!不是忽悠你,我抽了21年,一天一包有时不够,还不是一次性戒掉了。我鄙视戒几次还唧唧煤炭疯狂涨价会导致什么后果?现在煤炭价格高涨可不是仅仅局限中国,而是全世界的煤价都在快速上涨,因此这个能源短缺问题也就不是中国独有的问题了!因此,现在眼看得到的后果就有如下几个中国如果决定扩大进口,同样也会面