基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Spring 事务的隔离级别(Isolation)有哪些?

知识点图片

Spring 事务的隔离级别(Isolation Level)是为了解决在并发事务执行时可能出现的各种数据一致性问题(如脏读、不可重复读、幻读)。

需要强调的是,Spring 并不直接实现事务隔离级别,而是将其委托给底层的数据库去实现。Spring 的 Isolation 枚举类中定义了 5 种隔离级别(包含 4 种标准数据库隔离级别 + 1 个 Spring 特有默认级别)。

在详细介绍隔离级别之前,我们需要先了解并发事务会引发的 3 种常见问题:

  1. 脏读(Dirty Read): 事务 A 读取了事务 B 尚未提交的数据。如果事务 B 回滚,事务 A 读取到的就是无效数据。
  2. 不可重复读(Non-Repeatable Read): 事务 A 多次读取同一条记录,在此期间事务 B 修改(Update/Delete)并提交了该记录,导致事务 A 多次读取的结果不一致。
  3. 幻读(Phantom Read): 事务 A 按照某个条件多次查询数据,在此期间事务 B 插入(Insert)了符合该条件的新记录并提交,导致事务 A 后来查询时多出了“幻影”一样的记录。

Spring 的 5 种事务隔离级别

在 Spring 中,可以通过 @Transactional(isolation = Isolation.xxx) 来设置:

1. Isolation.DEFAULT(Spring 默认)

  • 含义: 使用底层数据库默认的隔离级别。
  • 说明: 如果你不显式设置隔离级别,Spring 就会按这个执行。对于 MySQL,默认是 REPEATABLE_READ;对于 Oracle / PostgreSQL / SQL Server,默认是 READ_COMMITTED

2. Isolation.READ_UNCOMMITTED(读未提交)

  • 含义: 最低的隔离级别。允许一个事务读取另一个事务尚未提交的数据。
  • 解决的问题: 无。
  • 存在的问题: 会出现脏读、不可重复读、幻读。
  • 应用场景: 极少使用,因为数据安全性太差。

3. Isolation.READ_COMMITTED(读已提交)

  • 含义: 保证一个事务只能读取到另一个事务已经提交的数据。
  • 解决的问题: 阻止了脏读
  • 存在的问题: 仍然会出现不可重复读和幻读。
  • 应用场景: 大多数主流数据库(如 Oracle、PostgreSQL)的默认隔离级别。

4. Isolation.REPEATABLE_READ(可重复读)

  • 含义: 保证在一个事务内多次读取同一数据时,结果始终是一致的(即便其他事务修改了该数据并提交)。
  • 解决的问题: 阻止了脏读和不可重复读
  • 存在的问题: 理论上仍然会出现幻读
  • 应用场景: MySQL (InnoDB) 的默认隔离级别(注:MySQL 的 InnoDB 引擎在这个级别下,通过 MVCC 和 Next-Key 间隙锁机制,实际上已经很大程度上解决了幻读问题)

5. Isolation.SERIALIZABLE(串行化)

  • 含义: 最高的隔离级别。完全服从 ACID 规范。它要求所有事务串行(排队)执行,不能并发。
  • 解决的问题: 阻止了脏读、不可重复读、幻读
  • 存在的问题: 性能极差,会导致大量的锁竞争和超时。
  • 应用场景: 极少使用,仅在对数据一致性要求极高且并发量极低的场景下使用。

总结与对比表

隔离级别 (Isolation) 脏读 (Dirty Read) 不可重复读 (Non-Repeatable) 幻读 (Phantom Read) 性能
READ_UNCOMMITTED 允许 允许 允许 最高
READ_COMMITTED 阻止 允许 允许 较高
REPEATABLE_READ 阻止 阻止 允许* 较低
SERIALIZABLE 阻止 阻止 阻止 最低

(注:星号 代表在标准 SQL 规范中允许幻读,但在 MySQL InnoDB 引擎中,REPEATABLE_READ 级别下通常不会发生幻读)*

代码示例

在 Spring Boot 中,通常这样使用:

java
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    // 设置为读已提交级别
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void updateUser() {
        // 业务逻辑
    }
}
00:00
00:00