首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【MySQL】事务 && ACID && 并行事务引发的问题 && 事务的隔离性与隔离级别

【MySQL】事务 && ACID && 并行事务引发的问题 && 事务的隔离性与隔离级别

原创
作者头像
lirendada
发布于 2026-05-20 10:38:11
发布于 2026-05-20 10:38:11
3480
举报
文章被收录于专栏:MySQLMySQL

Ⅰ. 初识事务

一、事务 && ACID

事务指把一组 SQL 语句打包成为一个整体,在这组 SQL 的执行过程中,要么全部成功,要么全部失败。这组 SQL 语句可以是一条也可以是多条。

一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(Consistency):一个事务的执行前后,数据必须保持一致,或者修改的数据必须符合预期。
  • 隔离性(Isolation):多个事务之间不能相互影响,通过不同的隔离级别对安全和性能做出平衡。
  • 持久性(Durability):事务处理结束后,对数据的修改必须保存到磁盘中,即便系统故障也不会丢失。

之所以要使用事务,是因为在日常的业务场景中大量存在需要用事务来保证安全的情况,而有支持事务的数据库能够简化我们的编程模型,让我们专注于业务逻辑本身,而不是关心底层的事务安全问题,简单的说,事务就是为了给应用层服务而产生的。

InnoDB 引擎通过什么技术来保证事务的这四个特性的呢❓❓❓

  • 原子性:undo log(回滚日志)
  • 持久性:redo log(重做日志)
  • 隔离性:MVCC(多版本并发控制)、锁机制
  • 一致性:由持久性、原子性、隔离性共同来保证

Ⅱ. 使用事务

一、语法

代码语言:javascript
复制
# 开始⼀个新的事务 
START TRANSACTION; 
# 或 
BEGIN; 

# 提交当前事务,并对更改持久化保存 
COMMIT;

# 回滚当前事务到某个保存点(若无指定保存点则回滚到最开始的状态),取消其更改
ROLLBACK [to 保存点];

# 设置保存点
SAVEPOINT 保存点名称

二、事务的提交方式

在 MySQL 中,自动提交(Auto Commit)和手动提交(Manual Commit)是两种不同的事务提交方式。查看当前事务的提交方式指令:

代码语言:javascript
复制
show variables like 'autocommit';
  1. 自动提交:
    1. 默认情况下,MySQL 处于自动提交模式。
    2. 每个 SQL 语句都会被立即提交到数据库,无需手动执行提交操作。当执行一条 SQL 语句后,该语句的结果就会立即生效,且无法回滚。
    3. 适用于简单的操作,如查询和更新单个记录。
  2. 手动提交:
    1. 手动提交模式需要显式地执行提交操作,以将一组相关的 SQL 语句作为一个事务进行提交。
    2. 在手动提交模式下,多个 SQL 语句可以作为一个原子操作进行提交,要么全部成功提交,要么全部回滚。
    3. 适用于需要保证数据的一致性和完整性的复杂操作,如插入、更新、删除多个相关记录。

通过以下语句设置事务为自动或手动提交:

代码语言:javascript
复制
# 设置事务自动提交 
SET AUTOCOMMIT=1;  # 方式一 
SET AUTOCOMMIT=ON; # 方式二 

# 设置事务手动提交 
SET AUTOCOMMIT=0;   # 方式一 
SET AUTOCOMMIT=OFF; # 方式二 

三、注意事项⭐⭐⭐

  1. 只要使用 START TRANSACTION 或 BEGIN 开启事务,就必须通过 COMMIT 提交才会持久化,与是否设置 set autocommit 无关。因为只要启动了事务,mysql 就会自动切换为手动提交模式。
  2. 手动提交模式下不用显式开启事务,在执行修改操作后,提交或回滚事务时直接使用 commit 或 rollback。
  3. 如果一个事务被提交了(commit),则不可以回滚(rollback)
  4. 回滚(rollback)不会结束事务。
  5. 当出现异常情况如客户端崩溃的时候,MySQL 会自动回滚。
  6. 在自动提交情况下,对于 InnoDB 每一条 SQL 语言都默认封装成事务,进行自动提交(select 有特殊情况,因为 MySQL 有 MVCC,后面会讲)

Ⅲ. 并行事务引发的问题

MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。那么在并发处理多个事务的时候,就可能出现 脏读、不可重复读、幻读 的问题。

一、脏读 dirty read

脏读是指一个事务读取了另一个事务可能尚未提交的数据。当一个事务读取了另一个事务的未提交数据时,如果另一个事务最终回滚,则读取到的数据实际上是无效的,没有意义。

比如在事务 A 中执行了一条 INSERT 语句,在没有执行 COMMIT 的情况下,会在事务 B 中被读取到,此时如果事务 A 执行回滚操作,那么事务 B 中读取到事务 A 写入的数据将没有意义,这种现象就是 "脏读" 。

二、不可重复读 non-repeatable read

不可重复读是指在一个事务内,多次读取同一数据时,得到的【结果】不一致。这是因为在事务执行期间,其它事务可能修改了被读取的数据。

例如,事务 A 先对某条数据进行了查询,之后事务 B 对这条数据进行了修改,并且提交事务,事务 A 再对这条数据进行查询时,得到了事务 B 修改之后的结果,虽然这不是 "脏读",但这导致了事务 A 在同一个事务中以相同的条件查询得到了不同的值,这种现象就是 "不可重复读"。

三、幻读 phantom read

幻读是指在一个事务内,多次执行同一个查询时,得到的【结果集】不一致。这是因为在事务执行期间,其他事务可能插入或删除了符合查询条件的数据。

例如,事务 A 查询了一个区间的记录得到结果集 A,事务 B 向这个区间的间隙中写入了一条记录并提交,事务 A 再查询这个区间的结果集时会查到事务 B 新写入的记录得到结果集 B,两次查询的结果集不一致,这种现象就是 "幻读"。

☠注意:虽然 InnoDB 在可重复读隔离级别中引入了 MVCC 和 next-key lock 两种解决方案,但可重复读级别还是避免不了幻读的问题,只是在很大程度上避免了幻读问题,要想真正解决幻读问题,还是需要使用可序列化级别!

幻读和不可重复读的区别如下所示:

  • 不可重复读 主要涉及到并发事务中的更新操作,而 幻读 主要涉及到并发事务中的插入和删除操作。
  • 不可重复读 关注的是查询结果的稳定性,而 幻读 关注的是查询结果的一致性。
    • 稳定性 关注的是系统的可靠性和鲁棒性,而 一致性 关注的是数据的正确性和一致性。

假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库查询账户余额大于 100 万的记录,发现共有 5 条,然后事务 B 也按相同的搜索条件也是查询出了 5 条记录。

接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事务,此时数据库超过 100 万余额的账号个数就变为 6。

然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,发现和前一次读到的记录数量不一样了,就感觉发生了幻觉一样,这种现象就被称为幻读。

Ⅳ. 事务的隔离性与隔离级别

一、概念

在数据库中,为了保证事务执行过程中尽量不受干扰,引入了一个重要特性:隔离性。

在数据库中,允许事务受不同程度的干扰,引入了一种重要特征:隔离级别。

按隔离级别高低排序如下:

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

二、四种隔离级别

  1. 读未提交(read uncommitted)
    1. 在该隔离级别中,所有的事务都可以看到其它事务没有提交的执行结果,相当于没有任何隔离性。这种隔离级别会会引起脏读、不可重复读、幻读。
  2. 读提交(read committed)
    1. 在该隔离级别中,一个事务只能看到其它已经提交的事务所做的改变。这种隔离级别会引起不可重复读、幻读。
  3. 可重复读(repeatable read)
    1. 这是 MySQL 的默认隔离级别,它确保同一个事务,在执行多次读取操作数据时,能看到同样的数据行。这种隔离级别会引起幻读。
  4. 串行化(serializable)
    1. 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,但是可能会导致超时和锁竞争(这种隔离级别太极端,导致并发效率不高,实际生产基本不使用)

三、查看和设置隔离级别

① 查看全局隔离级别

MySQL 默认使用全局隔离级别,这意味着所有会话都将使用相同的隔离级别。

代码语言:javascript
复制
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |   # 默认是读未提交级别
+-----------------------+

② 查看当前会话隔离级别

一共有两种方式,它们是等价的,只不过第二种写法是第一种写法的缩写而已!

代码语言:javascript
复制
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED       |
+------------------------+

mysql> select @@tx_isolation;                
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+

③ 设置隔离级别

代码语言:javascript
复制
# 通过 GLOBAL 或者 SESSION 分别指定不同作用域的事务隔离级别 
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode; 

# 隔离级别 
level: { 
      REPEATABLE READ  # 可重复读 
    | READ COMMITTED   # 读已提交 
    | READ UNCOMMITTED # 读未提交 
    | SERIALIZABLE     # 串行化 
} 

# 访问模式 
access_mode: { 
      READ WRITE  # 表示事务可以对数据进行读写 
    | READ ONLY   # 表示事务是只能读,不能写 
} 

# 示例:
#  设置全局事务隔离级别为串行化,后续所有事务生效,不影响当前事务 
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
#  设置会话事务隔离级别为串行化,当前会话后续的所有事务生效,不影响当前事务,可以在任何时候执行 
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
# 如果不指定任何作用域,设置只针对下一个事务,随后的事务恢复之前的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Ⅰ. 初识事务
    • 一、事务 && ACID
  • Ⅱ. 使用事务
    • 一、语法
    • 二、事务的提交方式
    • 三、注意事项⭐⭐⭐
  • Ⅲ. 并行事务引发的问题
    • 一、脏读 dirty read
    • 二、不可重复读 non-repeatable read
    • 三、幻读 phantom read
  • Ⅳ. 事务的隔离性与隔离级别
    • 一、概念
    • 二、四种隔离级别
    • 三、查看和设置隔离级别
      • ① 查看全局隔离级别
      • ② 查看当前会话隔离级别
      • ③ 设置隔离级别
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档