MySQL 事务隔离级别

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

截止目前, 星球 内专栏累计输出 66w+ 字,讲解图 2896+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2300+ 小伙伴加入学习 ,欢迎点击围观

事务隔离级别是数据库系统中用来定义多个事务之间如何相互影响的一种机制。MySQL 支持多种隔离级别,不同级别在性能和一致性上有所取舍。理解和合理使用事务隔离级别,对于构建高效且一致性的数据库系统至关重要。

什么是事务隔离级别?

在并发环境中,多个事务同时对数据库进行操作可能会引发以下问题:

  1. 脏读(Dirty Read)
    一个事务读取了另一个未提交事务的数据。

  2. 不可重复读(Non-repeatable Read)
    一个事务在多次读取同一数据时,由于其他事务的更新,导致读取结果不一致。

  3. 幻读(Phantom Read)
    一个事务在多次查询时,由于其他事务插入或删除数据,导致结果集的行数发生变化。

为了解决这些问题,SQL 标准定义了四种事务隔离级别。

MySQL 的四种事务隔离级别

1. 读未提交(READ UNCOMMITTED)

  • 特点
    一个事务可以读取到其他事务未提交的数据(脏读)。

  • 问题
    容易引发脏读。

  • 适用场景
    需要高并发、数据一致性要求不高的场景。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

2. 读已提交(READ COMMITTED)

  • 特点
    一个事务只能读取到其他事务已经提交的数据。

  • 问题
    避免了脏读,但可能出现不可重复读。

  • 适用场景
    大多数关系数据库的默认隔离级别,如 Oracle。

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

3. 可重复读(REPEATABLE READ)

  • 特点
    一个事务在开始时会创建一致性视图,确保多次读取同一数据时结果一致。

  • 问题
    避免了脏读和不可重复读,但可能出现幻读。
    MySQL 通过 间隙锁(Gap Lock) 解决幻读问题。

  • 适用场景
    MySQL 的默认隔离级别,适合大多数应用场景。

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

4. 串行化(SERIALIZABLE)

  • 特点
    最高级别的隔离,强制事务顺序执行,完全避免脏读、不可重复读和幻读。

  • 问题
    并发性能极低,通常只在对数据一致性要求极高的场景使用。

  • 适用场景
    银行转账等关键性操作。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

隔离级别对比

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读(默认级别)不可能不可能不可能(MySQL 间隙锁解决)
串行化不可能不可能不可能

示例演示

假设有如下表 t_student

CREATE TABLE t_student (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    score INT
);

1. 测试脏读

事务 A 修改数据未提交:

-- 事务 A
START TRANSACTION;
UPDATE t_student SET score = 90 WHERE id = 1;

事务 B 读取数据:

-- 事务 B
SELECT * FROM t_student WHERE id = 1;

若隔离级别为 READ UNCOMMITTED,事务 B 会读到事务 A 未提交的数据。

2. 测试不可重复读

事务 A 读取数据两次,期间事务 B 修改了数据:

-- 事务 A
START TRANSACTION;
SELECT * FROM t_student WHERE id = 1;

-- 事务 B
UPDATE t_student SET score = 85 WHERE id = 1;
COMMIT;

-- 事务 A
SELECT * FROM t_student WHERE id = 1;

若隔离级别为 READ COMMITTED,事务 A 的两次查询结果可能不同。

3. 测试幻读

事务 A 查询范围数据,期间事务 B 插入新数据:

-- 事务 A
START TRANSACTION;
SELECT * FROM t_student WHERE score > 80;

-- 事务 B
INSERT INTO t_student VALUES (3, 'John', 95);
COMMIT;

-- 事务 A
SELECT * FROM t_student WHERE score > 80;

若隔离级别为 REPEATABLE READSERIALIZABLE,事务 A 能避免幻读。

设置事务隔离级别

全局设置

-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

会话设置

-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

查看当前隔离级别:

SELECT @@transaction_isolation;

小结

MySQL 提供的四种事务隔离级别在一致性和性能之间提供了灵活选择。开发者应根据业务需求选择合适的隔离级别,以平衡数据一致性和系统性能。在使用事务时,还需注意并发场景下的性能优化与锁问题,以避免死锁和资源争夺。