mysql菜鸟教程

专栏导航

15.4 事务隔离级别

      在上一节中,我们学习了事务的 ACID 特性,其中 隔离性(Isolation) 指的是多个事务并发执行时,一个事务的执行不应被其他事务干扰。然而,完全隔离事务往往意味着性能的极大损失,因为事务必须串行执行。因此,数据库系统提供了不同的隔离级别,允许开发者在数据一致性和并发性能之间做出权衡。

一、为什么需要隔离级别?

当多个事务同时操作同一份数据时,如果不加控制,可能会出现以下三种常见的并发问题

问题

描述

类比

脏读

一个事务读到了另一个事务

未提交

的数据。如果那个事务后来回滚,则读到的数据是无效的。

你看到朋友在写购物清单,但还没写完,你就照着清单去买,结果朋友最后划掉了其中一项,你多买了东西。

不可重复读

在一个事务内,两次读取

同一行数据

,得到的结果不一致(因为中间被其他事务

修改并提交

了)。

你在统计余额,第一次查询是 1000 元,此时家人存了 500 元并提交,你第二次查询变成 1500 元,导致统计错误。

幻读

在一个事务内,两次执行

范围查询

,第二次查询返回了第一次没有出现的行(因为中间被其他事务

插入或删除

了行)。

你在统计所有工资大于 5000 的员工,第一次查出 10 人,此时新入职一名工资 8000 的员工并提交,第二次查出 11 人,仿佛出现了幻觉。

为了解决这些问题,SQL 标准定义了四个隔离级别。级别越高,数据一致性越好,但并发性能越低。

二、四种隔离级别

隔离级别

脏读

不可重复读

幻读

说明

读未提交

可能

可能

可能

最低级别,几乎不加锁,性能最高,但数据最不可靠。

读已提交

避免

可能

可能

大多数数据库的默认级别(如 Oracle、PostgreSQL),只能读到已提交的数据。

可重复读

避免

避免

可能(MySQL InnoDB 通过间隙锁避免了幻读)

MySQL InnoDB 的默认级别,保证同一事务多次读取同一数据结果一致。

可串行化

避免

避免

避免

最高级别,事务完全串行执行,性能最低。

下面我们详细介绍每种级别,并用示例说明其行为。

1. 读未提交(READ UNCOMMITTED)

在此级别下,一个事务可以看到其他事务未提交的修改。这可能导致脏读。

示例

事务 A 和事务 B 同时进行。

  • 事务 A 开始,将 account 表中张三的余额从 1000 改为 900(未提交)。

  • 事务 B 开始,读取张三的余额,得到 900(脏读)。

  • 事务 A 回滚,余额恢复为 1000。

  • 事务 B 后续操作基于错误的 900 进行,产生错误。

使用场景:几乎不用,除非对数据一致性要求极低且追求极致性能(比如某些统计类应用)。

2. 读已提交(READ COMMITTED)

在此级别下,一个事务只能看到其他事务已经提交的修改。这避免了脏读,但可能出现不可重复读。

示例

  • 事务 A 开始,查询张三余额,得到 1000。

  • 事务 B 开始,将张三余额改为 900 并提交。

  • 事务 A 再次查询张三余额,得到 900(不可重复读,因为两次读取结果不同)。

使用场景:许多数据库的默认级别,适合对一致性有一定要求但允许短暂不一致的业务。

3. 可重复读(REPEATABLE READ)

在此级别下,一个事务在执行期间看到的数据始终保持一致。即第一次读取后,后续再读同一行,结果不变。这避免了脏读和不可重复读。

MySQL 的 InnoDB 引擎通过 多版本并发控制(MVCC) 实现可重复读。在可重复读级别下,InnoDB 还通过 间隙锁(gap lock) 机制部分解决了幻读问题,使得大多数情况下幻读不会发生。

示例

  • 事务 A 开始,查询工资大于 5000 的员工,返回 10 行。

  • 事务 B 开始,插入一个工资 8000 的员工并提交。

  • 事务 A 再次查询工资大于 5000 的员工,仍然只看到 10 行(没有幻读,因为 InnoDB 的间隙锁阻止了插入)。

但在某些极端的并发场景下,仍可能发生幻读。如果绝对避免幻读,需要提升到可串行化。

使用场景:MySQL 默认级别,适合大多数业务,特别是需要一致性读的场景(如报表生成、统计分析)。

4. 可串行化(SERIALIZABLE)

此级别强制事务串行执行,所有并发问题都得以避免。实现方式通常是对所有读取的数据加锁(包括间隙锁),这会导致并发性能急剧下降。

示例

  • 事务 A 开始,查询工资大于 5000 的员工,会对满足条件的行以及间隙加锁。

  • 事务 B 试图插入工资 8000 的员工,会被阻塞,直到事务 A 提交或回滚。

使用场景:对数据一致性要求极其严格,且并发量很低的场景(如金融交易中的某些关键操作)。

三、在 MySQL 中设置隔离级别

查看当前隔离级别

-- 查看全局或会话的隔离级别
SELECT @@global.transaction_isolation;
SELECT @@session.transaction_isolation;
-- 或老版本语法
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;

设置隔离级别

-- 设置会话级(仅当前会话有效)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 设置全局级(所有新会话生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

示例:测试不同隔离级别的行为

为了直观感受不同隔离级别的差异,可以开启两个 MySQL 客户端窗口(会话 A 和会话 B)进行实验。

准备数据

CREATE TABLE account (    id INT PRIMARY KEY,    balance INT ); INSERT INTO account VALUES (1, 1000);

实验1:读未提交下的脏读

  • 会话 A:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

  • 会话 B:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

  • 会话 A:START TRANSACTION; UPDATE account SET balance = 900 WHERE id = 1;

  • 会话 B:SELECT * FROM account; 可以看到 balance = 900(脏读)

  • 会话 A:ROLLBACK; 此时会话 B 读到的 900 是无效的。

实验2:读已提交下避免脏读,但出现不可重复读

  • 会话 A:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

  • 会话 B:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

  • 会话 A:START TRANSACTION; SELECT * FROM account; 得到 1000。

  • 会话 B:START TRANSACTION; UPDATE account SET balance = 900 WHERE id = 1; COMMIT;

  • 会话 A:再次 SELECT * FROM account; 得到 900(不可重复读)。

实验3:可重复读下避免不可重复读

  • 会话 A:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

  • 会话 B:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

  • 会话 A:START TRANSACTION; SELECT * FROM account; 得到 1000。

  • 会话 B:UPDATE account SET balance = 900 WHERE id = 1; COMMIT;

  • 会话 A:再次 SELECT * FROM account; 仍然得到 1000(因为 MVCC 提供了事务开始时的快照)。此时会话 B 的修改对会话 A 不可见,直到会话 A 结束事务。

小结

  • 隔离级别定义了事务之间数据可见性的程度,需要在一致性和性能之间权衡。

  • 读未提交:允许脏读,几乎不用。

  • 读已提交:避免脏读,但可能出现不可重复读和幻读。

  • 可重复读:MySQL 默认级别,避免脏读和不可重复读,InnoDB 通过间隙锁基本避免幻读。

  • 可串行化:完全隔离,但性能最低。

  • 使用 SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL 可以调整隔离级别。

  • 理解隔离级别有助于分析和解决并发环境下的数据一致性问题。


所有评论

关于我 备案号:蜀ICP备2023042032号-1