Mysql面试题分享二十三:Mysql事物实现原理?

一、前言

MySQL是一种常用的关系型数据库管理系统,它支持事务的概念和实现。事务是一组数据库操作的集合,这些操作要么全部成功执行,要么全部失败回滚,以保证数据的一致性和完整性。在MySQL中,事务的实现原理主要包括以下几个方面:

  • 原子性(Atomicity):事务中的所有操作要么全部成功执行,要么全部失败回滚。MySQL通过日志(log)来实现原子性。在事务执行之前,MySQL会将事务的操作记录到日志中,如果事务执行失败或者回滚,可以通过日志进行恢复。
  • 一致性(Consistency):事务执行前后,数据库的状态必须保持一致。MySQL通过锁(lock)来实现一致性。在事务执行期间,MySQL会对相关的数据进行锁定,防止其他事务对其进行修改,保证数据的一致性。
  • 隔离性(Isolation):事务的执行不受其他事务的干扰。MySQL通过锁和并发控制机制来实现隔离性。在事务执行期间,MySQL会对相关的数据进行锁定,防止其他事务对其进行修改,保证事务的隔离性。
  • 持久性(Durability):事务一旦提交,其结果就是永久性的。MySQL通过日志(log)和数据持久化机制来实现持久性。在事务提交之后,MySQL会将事务的操作记录到日志中,并将数据持久化到磁盘上,以保证数据的持久性。

MySQL事务的实现原理主要包括日志、锁和并发控制机制以及数据持久化机制。通过这些机制的配合和协调,MySQL能够保证事务的原子性、一致性、隔离性和持久性,从而确保数据的完整性和一致性。

二、并行事务会引发什么问题?

MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。那么在同时处理多个事务的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题**。接下来,通过举例子给大家说明,这些问题是如何发生的。

脏读

如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。
举个栗子。
假设有 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 )**;会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

按隔离水平高低排序如下:

针对不同的隔离级别,并发事务时可能发生的现象也会不同。

也就是说:

  • 在「读未提交」隔离级别下,可能发生脏读、不可重复读和幻读现象;

  • 在「读提交」隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;

  • 在「可重复读」隔离级别下,可能发生幻读现象,但是不可能脏读和不可重复读现象;

  • 在「串行化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。

所以,要解决脏读现象,就要升级到「读提交」以上的隔离级别;要解决不可重复读现象,就要升级到「可重复读」的隔离级别。不过,要解决幻读现象不建议将隔离级别升级到「串行化」,因为这样会导致数据库在并发事务时性能很差。InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它通过next-key lock 锁(行锁和间隙锁的组合)来锁住记录之间的“间隙”和记录本身,防止其他事务在这个记录之间插入新的记录,这样就避免了幻读现象。接下里,举个具体的例子来说明这四种隔离级别,有一张账户余额表,里面有一条记录:然后有两个并发的事务,事务 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

接下来详细说下,「读提交」和「可重复读」隔离级别到底怎样实现的,Read View 又是如何工作的?

可重复读隔离级别是如何实现的?

「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View。想要知道可重复读隔离级别是如何实现的,我们需要了解两个知识:

  • 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 值

  • creator_trx_id :指的是创建该 Read View 的事务的事务 id

知道了 Read View 的字段,我们还需要了解聚族索引记录中的两个隐藏列,假设在账户余额表插入一条小林余额为 100 万的记录,然后我把这两个隐藏列也画出来。

对于使用 InnoDB 存储引擎的数据库表,它的聚族索引记录中都包含下面两个隐藏列:

  • trx_id,当一个事务对某条聚族索引记录进行改动时,就会把该事务的事务 id 记录在 trx_id 隐藏列里

  • roll_pointer,每次对某条聚族索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录。

了解完这两个知识点后,就可以跟大家说说可重复读隔离级别是如何实现的。假设事务 A 和 事务 B 差不多同一时刻启动,那这两个事务创建的 Read View 如下:

事务 A 和 事务 B 的 Read View 具体内容如下:

  • 在事务 A 的 Read View 中,它的事务 id 是 51,由于与事务 B 同时启动,所以此时活跃的事务的事务 id 列表是 51 和 52,活跃的事务 id 中最小的事务 id 是事务 A 本身,下一个事务 id 应该是 53。

  • 在事务 B 的 Read View 中,它的事务 id 是 52,由于与事务 A 同时启动,所以此时活跃的事务的事务 id 列表是 51 和 52,活跃的事务 id 中最小的事务 id 是事务 A,下一个事务 id 应该是 53。

然后让事务 A 去读账户余额为 100 万的记录,在找到记录后,它会先看这条记录的 trx_id,此时发现 trx_id 为 50,通过和事务 A 的 Read View 的 m_ids 字段发现,该记录的事务 id 并不在活跃事务的列表中,并且小于事务 A 的事务 id,这意味着,这条记录的事务早就在事务 A 前提交过了,所以该记录对事务 A 可见,也就是事务 A 可以获取到这条记录。接着,事务 B 通过 update 语句将这条记录修改了,将小林的余额改成 200 万,这时 MySQL 会记录相应的 undo log,并以链表的方式串联起来,形成版本链,如下图:

你可以在上图的「记录字段」看到,由于事务 B 修改了该记录,以前的记录就变成旧版本记录了,于是最新记录和旧版本记录通过链表的方式串起来,而且最新记录的 trx_id 是事务 B 的事务 id。然后如果事务 A 再次读取该记录,发现这条记录的 trx_id 为 52,比自己的事务 id 还大,并且比下一个事务 id 53 小,这意味着,事务 A 读到是和自己同时启动事务的事务 B 修改的数据,这时事务 A 并不会读取这条记录,而是沿着 undo log 链条往下找旧版本的记录,直到找到 trx_id 等于或者小于事务 A 的事务 id 的第一条记录,所以事务 A 再一次读取到 trx_id 为 50 的记录,也就是小林余额是 100 万的这条记录。「可重复读」隔离级别就是在启动时创建了 Read View,然后在事务期间读取数据的时候,在找到数据后,先会将该记录的 trx_id 和该事务的 Read View 里的字段做个比较:

  • 如果记录的 trx_id 比该事务的 Read View 中的 creator_trx_id 要小,且不在 m_ids 列表里,这意味着这条记录的事务早就在该事务前提交过了,所以该记录对该事务可见;

  • 如果记录的 trx_id 比该事务的 Read View 中的 creator_trx_id 要大,且在 m_ids 列表里,这意味着该事务读到的是和自己同时启动的另外一个事务修改的数据,这时就不应该读取这条记录,而是沿着 undo log 链条往下找旧版本的记录,直到找到 trx_id 等于或者小于该事务 id 的第一条记录。

就是通过这样的方式实现了,「可重复读」隔离级别下在事务期间读到的数据都是事务启动前的记录。这种通过记录的版本链来控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。

读提交隔离级别是如何实现的?

「读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。那读提交隔离级别是怎么实现呢?我们还是以前面的例子来聊聊。假设事务 A 和 事务 B 差不多同一时刻启动,然后事务 B 将小林的账户余额修改成了 200 万,但是事务 B 还未提交,这时事务 A 读到的数据,应该还是小林账户余额为 100 万的数据,那具体怎么做到的呢?

事务 A 在找到小林这条记录时,会看这条记录的 trx_id,发现和事务 A 的 Read View 中的 creator_trx_id 要大,而且还在 m_ids 列表里,说明这条记录被事务 B 修改过,而且还可以知道事务 B 并没有提交事务,因为如果提交了事务,那么这条记录的 trx_id 就不会在 m_ids 列表里。因此,事务 A 不能读取该记录,而是沿着 undo log 链条往下找。当事务 B 修改数据并提交了事务后,这时事务 A 读到的数据,就是小林账户余额为 200 万的数据,那具体怎么做到的呢?

事务 A 在找到小林这条记录时,会看这条记录的 trx_id,发现和事务 A 的 Read View 中的 creator_trx_id 要大,而且不在 m_ids 列表里,说明该记录的 trx_id 的事务是已经提交过的了,于是事务 A 就可以读取这条记录,这也就是所谓的读已提交机制。

四、 核心日志

1、Redo Log:

保持继续执行恢复没有写入的功能日志,物理日志,innoDB独有。

1)、基本原理:

redoLogBuffer ->持久化,redoLog file ->(已经提交,还没写入IBD文件的数据)

2)、刷盘规则:

Redo Log Buffer(是存放在操作系统的用户空间中) ->通过操作系统的内核空间缓冲区(OS buffer)-> fsync()->logfile

有一个O_DIRECT标志位,可以不通过OS BUFFER。但是redoLog没有使用。

物理日志,顺序循环,最后一个文件写满会向第一个文件写数据,也就是覆盖写。

3)、RedoLog的LSN机制

log sequence number(日志的逻辑序列号,8字节的存储空间,单调递增)

  • RedoLog写入数据的总量
  • 检查点位置
  • 数据页版本相关的信息

机制

每个数据页,表头有一个fil_page_lsn,记录当前最终的LSN值。

将数据页的LSN与RedoLog的LSN值比较,如果数据页小于log,就是表示丢失了一部分数据,这个时候就用redolog来恢复数据,不然就不恢复。

2、Undo Log:

回滚操作,生成与执行相反的逻辑日志,且提前会有快照放在undoLog里面,有备份。

1)、 简介

作用:回滚事务

多版本并发事务(MVCC)

启动事务之前,修改的数据记录存储到UndoLog中,如果事务回滚或MySql数据库崩溃,可以利用 UndoLog对没有提交的事务进行回滚,保证数据库的数据一致性。

UndoLog消失

undoLog产生于事务开始前,提交不会立刻删除,会放到待删除列表,让purge thread进行删除处理

undoLog机制

逻辑日志

  • 数据库执行insert,undoLog记录一条Delete;
  • 数据库执行一条Delete,UndoLog会记录一条对应的Insert语句;
  • 当数据库执行一条update语句时,undoLog会记录一条相反的update语句。

UndoLog需要持久化,会产生redoLog。

undoLog的可靠性和完整性需要RedoLog来保证;

因此数据库崩溃先做RedoLog数据恢复,然后做UndoLog回滚。

2)、基本原理

都需要经过OS Buffer持久化。

如果日志中,设置了O_DIRECT的标志位,可以不经过OS BUFFER。

1.2.2 实现MVCC机制

多版本控制(MVCC)

  • 提交之前,向UndoLog保存事务当前的数据,这些保存到UndoLog的旧版本数据可以作为快照供其他并发事务进行快照读。
  • select语句查询的数据被其他事务锁定,可以从UndoLog分析出当前数据之前的版本,从而向客户端返回之前的数据。

undo 回滚段,分为 insert 和 update;

  • insert undo log:事务对插入新记录产生的undo log,只在事务回滚时需要,在事务提交后可以立即丢弃。(但是没有丢吧?有点问题)
  • update undo log:进行删除和更新操作产生的undoLog,不仅回滚需要,一致性读也需要,因此不能随便删除,只有当数据库所使用的快照不涉及该日志记录,对应的回滚日志才会被purge线程删除。

可重复读隔离级别:MVVC

数据库每行数据后面添加了3个字段,

  • 字节的事务id(DB_TRX_ID)字段;记录最后一次修改本行记录的事务id
  • 字节的回滚指针(DB_ROLL_PTR)字段, 指向上一个版本的行记录(通过undo 找到相应的数据)
  • 字节的DB_ROW_ID字段。(聚集索引行id)

3、BinLog:

二进制日志,mysql自身日志,主要是主从复制,数据恢复(使用mysqlbinlog)。

1)、 简介

binlog,二进制日志。

记录mysql所有数据库表结构变更和表数据变更的二进制日志。

不会记录注入select和show这类查询操作的日志 。

同时,以事件形式记录相关变更操作的,并且包含语句执行消耗的时间。

2)、 重要使用场景

主从复制:开启BinLog,主数据库把BinLog发送到从数据库,从数据库获取BinLog,

通过IO线程将日志写到中继日志,也就是RelayLog。通过sql线程将relayLog中的数据同步到从数据库,达到主从数据库的一致性。

数据恢复:mysql数据库故障或者崩溃, BinLog进行数据恢复,例如可以使用mysqlbinlog等工具进行数据恢复

3)、 BinLog记录模式

Row

记录每一行,非常清楚记录每一行的修改情况。

优点:完全实现主从同步和数据恢复

缺点: 大批量操作,二进制内容日志暴涨,影响主从数据库的同步性能

Statement

记录每一条修改数据的sql语句。

SQL进程将BinLog中的sql语句解析成和Mysql主数据库中执行过的sql语句相同的sql语句,然后在从数据库执行SQL进程解析出来的sql语句。

优点:不记录数据的修改细节,只是记录数据表结构和数据变更的sql语句,产生日志小,减少IO操作,提高数据存储和恢复的效率。

缺点:某些情况,主从不一致。比如主数据库使用了last_insert_id(),和now()等函数,会导致mysql主从数据库中的数据不一样。

Mixed

row和statement一起用。

使用Statement模式保存BinLog,存在state模式无法复制的操作,例如使用last_insert_id(),和now()等函数,会使用Row模式。

Mysql会根据执行的sql语句选择写入的记录模式

4)、 BinLog文件结构

各种更新操作。

修改操作:Log Event

Query Event,Row Event, Xid Event。

文件结构,

timestamp,type_code,server_id,event_length,next_position,flags,extra_headers

5)、 BinLog写入机制

先记录BinLog 二进制日志,再记录事务日志。

BinLog就是各种日志事件的集合。

操作:

(1)根据记录的模式(Row,Statement,Mixed)和操作(create,drop,alter,insert,update)等触发事件,生成日志事件(事件触发执行机制)

(2)将日志事件写入相应的缓冲区(binlog_cache_mngr),

这个数据结构有两个缓冲区,一个是stmt_cache,不支持事务的信息;

一个是trx_cache,用于存放支持事务的信息。

(3)事务commit阶段,会将日志文件写入磁盘的BinLog中。

不同的事务会以串行的方式将日志事件写入BinLog,所以一个事务包含的日志事件在BinLog文件中是连续的,中间不会插入其他事务的日志事件。

综上,一个事务的BinLog是完整的,并且中间不会插入其他事务的BinLog

6)、 BinLog的组提交机制

提高Mysql日志刷盘的效率,Mysql数据库提供了组提交(group commit)的功能。

通过组提交功能,调用一次fsync()函数将多个事务的日志刷新到磁盘的日志文件中,而不是单个事务的日志单独刷新,提升刷盘效率。

提交事务。

两个阶段的操作:

  • 修改内存中的事务信息,并且写入相应的RedoLogBffer
  • 调用fsync(),将redo log buffer中的日志信息刷新到磁盘的Redo log文件中。

BinLog,5.6之前会失效,mysql需要保证binlog 和事务日志的一致性,使用了两阶段事务。

(1)事务提交,Innodb 存储引擎需要进行prepare操作。

(2)将更新写入BinLog操作。

(3)将事务日志写入RedoLog。

保证一致性的话,prepare阶段,会启用一个prepare commit mutex锁,会导致开启二进制日志后 组提交功能 失效。

5.6之后,会将事务按照一定的顺序放入一个队列当中,第一个事务称leader,其他事务叫follower.

移除了prepare commit mutex锁。

实现方式成为 二进制日志组提交(Binary Log Group Commit)BLGC.

(1) flush,将每个事务的BinLog写入对应的内存缓冲区

(2)sync,将内存缓冲区的binlog写入磁盘的binlog,如果队列有多个事务,此时只执行一次刷盘操作就可以将多个事务的binlog刷新到磁盘的binlog。

(3)commit,leader事务根据队列中事务的顺序调用的存储引擎层事务的提交操作。

五、 Mysql事务流程

1 mysql事务执行流程

MySQL Innodb Engine--DML操作时先生成Undo Log还是先生成Redo Log

答案:先生成Undo Log,再生成Redo Log。

在生成Undo Log并写入到Undo Space时,会产生Redo Log。

在故障恢复时,可以通过Redo Log来恢复Undo Log,再通过Undo Log来回滚事务。

2 Mysql事务恢复流程

事务提交之前,

会先使用RedoLog,先做恢复数据,然后使用undolog回滚数据,

事务提交之后,

会使用redoLog恢复数据。

先恢复看看,如果提交了就结束

没有提交就undo.

3 Mysql的XA事务

mysql5.0.3版本支持XA事务,只有Innodb支持。 connector/j 5.0.0版本之后支持XA事务。

1)、 XA事务的基本原理

XA事务支持不同数据库之间实现分布式事务。

XA事务其实是二阶段提交的分布式事务。

使用XA事务,数据库事务隔离级别需要设置成串行化。

XA事务组成:

  • 事务管理器 控制所有事务的协调者。
  • 资源管理器 所有数据库连接。
  • 应用程序
2)、 XA事务语法

查看存储引擎是否支持XA事务

show engines \G

XA STARTXA END xidXA PREPARE xidXA COMMIT xidXA ROLLBACK xidXA RECOVER (查询)

XA事务使用XID标识分布式事务,xid主要由以下几部分组成。

xid:gtrid [,bqual[,format Id]]

gtrid: 必须,字符串,表示全局事务标识符

bqual:可选,字符串,默认空串,表示分支限定符,

formatId:可选,默认1,用于标识gtrid和bqual值使用的格式,

六、总结

事务是在 MySQL 引擎层实现的,我们常见的 InnoDB 引擎是支持事务的,事务的四大特性是原子性、一致性、隔离性、持久性。当多个事务并发执行的时候,会引发脏读、不可重复读、幻读这些问题,那为了避免这些问题,SQL 提出了四种隔离级别,分别是读未提交、读已提交、可重复读、串行化,从左往右隔离级别顺序递增,隔离级别越高,意味着性能越差,InnoDB 引擎的默认隔离级别是可重复读。要解决脏读现象,就要将隔离级别升级到读已提交以上的隔离级别,要解决不可重复读现象,就要将隔离级别升级到可重复读以上的隔离级别。而对于幻读现象,不建议将隔离级别升级为串行化,因为这会导致数据库并发时性能很差。InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它通过next-key lock 锁(行锁和间隙锁的组合)来锁住记录之间的“间隙”和记录本身,防止其他事务在这个记录之间插入新的记录,这样就避免了幻读现象。对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 **Read View **来实现的,它们的区别在于创建 Read View 的时机不同:

  • 读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。

  • 「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。

这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列」的比对,来控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值