Spring 事务隔离级别

xiaopeng
发布于 2026-08-21 / 1 阅读
0
0

Spring 事务隔离级别

Spring 事务隔离级别 ⭐⭐⭐⭐⭐

面试频率:★★★★★
工作频率:★★★★☆


⚡30 秒速记

🟢 一句话

事务隔离级别决定多个事务并发执行时,一个事务能够看到其他事务什么状态的数据。

并发事务常见三个问题:

脏读
= 读到了别人还没提交的数据

不可重复读
= 同一事务中,同一行数据前后读取结果不同

幻读
= 同一事务中,两次条件查询,符合条件的行发生变化

四种隔离级别:

READ_UNCOMMITTED
        ↓
READ_COMMITTED
        ↓
REPEATABLE_READ
        ↓
SERIALIZABLE

隔离越来越强
并发能力通常越来越低

一、为什么需要事务隔离?

假设数据库:

余额 = 1000

现在:

事务A                     事务B

查询余额1000
                           查询余额1000

修改余额
                           再次查询

两个事务同时操作数据库时:

会产生很多问题。

例如:

A还没提交的数据

B能不能看到?

或者:

B第一次查1000

A修改并提交

B第二次查

应该看到1000还是500?

这些问题:

就是:

并发事务问题

Spring / 数据库通过:

事务隔离级别

控制事务之间的数据可见性。


二、脏读 Dirty Read ⭐⭐⭐⭐⭐

一句话:

读取到了其他事务还没有提交的数据。

例如:

数据库:

余额 = 1000

事务A:

查询余额1000

↓

修改余额500

↓

还没有commit

这时候事务B:

查询余额

↓

500

但是事务A突然:

rollback

数据库又变成:

余额 = 1000

那么事务B刚才读到的:

500

其实是:

从来没有真正提交的数据

所以叫:

脏读。


流程

事务A                     事务B

余额1000

修改成500
   ↓
未提交
                           ↓
                         查询
                           ↓
                         500
   ↓
rollback
   ↓
恢复1000

事务B:

读到了一个最终不存在的数据

速记

脏读

=

读到了别人未提交的数据

关键词:

未提交。


三、不可重复读 Non-repeatable Read ⭐⭐⭐⭐⭐

一句话:

同一个事务中,两次读取同一行数据,结果不一样。

例如:

事务A:

第一次查询:

张三余额 = 1000

这时候事务B:

修改张三余额

1000 → 500

↓

commit

事务A:

再次查询:

张三余额 = 500

结果:

第一次:

1000


第二次:

500

同一个事务:

同一行数据:

两次读取结果不同

这叫:

不可重复读。


流程

事务A                     事务B

查询张三
余额1000

                           修改张三
                           余额500
                              ↓
                           commit

再次查询张三
余额500

速记

不可重复读

=

同一行数据

↓

被别人修改并提交

↓

前后读取结果不同

关键词:

修改。


四、幻读 Phantom Read ⭐⭐⭐⭐⭐

一句话:

同一个事务中,两次按照相同条件查询,符合条件的数据行发生变化。

例如:

事务A:

SELECT *
FROM user
WHERE age > 18;

第一次:

10个人

这时候事务B:

新增一个:

age = 20

↓

commit

事务A:

再次执行:

SELECT *
FROM user
WHERE age > 18;

结果:

11个人

事务A:

第一次:

10行


第二次:

11行

就像:

突然多出来一行

所以叫:

幻读。


流程

事务A                     事务B

查询age > 18

10个人

                           新增一个20岁的人

                           commit

再次查询age > 18

11个人

速记

幻读

=

条件查询

↓

符合条件的数据行发生变化

关键词:

行发生变化 / 新增删除。


五、不可重复读和幻读区别 ⭐⭐⭐⭐⭐

这个面试非常容易问。


不可重复读

关注:

同一行数据的内容变了

例如:

第一次:

张三 1000


第二次:

张三 500

还是:

张三这一行

但是:

这一行的数据被修改了

幻读

关注:

符合条件的数据行发生变化

例如:

第一次:

10个人


第二次:

11个人

出现了:

新的符合条件的数据行

一句话区别

不可重复读

=

同一行的数据变了


幻读

=

符合条件的行变了

可以粗略记:

不可重复读 → UPDATE

幻读 → INSERT / DELETE

注意:

这只是帮助记忆,不是严格定义。


六、三个问题最终对比 ⭐⭐⭐⭐⭐

脏读

↓

别人没提交

我就读到了
不可重复读

↓

别人修改并提交

↓

我前后读同一行不一样
幻读

↓

别人新增 / 删除符合条件的数据并提交

↓

我前后条件查询的结果集发生变化

七、四种事务隔离级别 ⭐⭐⭐⭐⭐

SQL 标准定义四种主要隔离级别:

READ_UNCOMMITTED

READ_COMMITTED

REPEATABLE_READ

SERIALIZABLE

隔离强度:

READ_UNCOMMITTED
        ↓
READ_COMMITTED
        ↓
REPEATABLE_READ
        ↓
SERIALIZABLE

越来越强

通常:

隔离越强

↓

并发限制越多

↓

性能 / 并发能力可能越低

八、READ_UNCOMMITTED

中文:

读未提交。

意思:

其他事务还没有commit

↓

我就可以读取它修改的数据

例如:

事务A:

1000 → 500

还没commit


事务B:

已经能读到500

所以:

可能发生:

脏读 ❌

不可重复读 ❌

幻读 ❌

这里:

❌ = 不能防止

隔离级别最低。


九、READ_COMMITTED ⭐⭐⭐⭐⭐

中文:

读已提交。

意思:

其他事务必须commit以后

↓

我才能读取到它的数据

所以:

事务A:

1000 → 500

还没commit

事务B:

看不到500

只有:

事务A commit

以后:

事务B才能看到:

500

因此:

脏读:

✅ 可以防止

但是:

不可重复读:

❌ 仍可能发生

例如:

事务A:

第一次查询1000


事务B:

修改500

commit


事务A:

再次查询500

十、REPEATABLE_READ ⭐⭐⭐⭐⭐

中文:

可重复读。

核心:

在同一个事务中的一致性读取,可以保持稳定的读取视图。

例如:

事务A:

第一次查询余额1000

事务B:

修改余额500

↓

commit

事务A再次进行一致性读取:

仍然可以看到1000

所以:

可以防止:

脏读 ✅

不可重复读 ✅

十一、MySQL 默认隔离级别 ⭐⭐⭐⭐⭐

MySQL InnoDB 默认:

REPEATABLE READ

简称:

RR

MySQL InnoDB:

还通过:

MVCC

以及在特定锁定读 / 写操作中的:

锁机制

处理很多并发读取和幻读相关问题。

所以面试时:

不要简单说:

MySQL RR一定会发生幻读

更准确:

SQL 标准层面,REPEATABLE READ 主要保证避免脏读和不可重复读;MySQL InnoDB 在 RR 下结合 MVCC 和锁机制,对幻读也做了进一步处理。


十二、SERIALIZABLE

中文:

串行化。

这是:

最高的标准隔离级别

可以粗略理解:

事务A
 ↓
正在执行

事务B
 ↓
某些冲突操作需要等待

事务A结束
 ↓
事务B继续

它会对并发事务施加更严格的限制。

所以:

并发问题最少

但是

并发能力通常最低

可以防止:

脏读 ✅

不可重复读 ✅

幻读 ✅

十三、四种隔离级别对比 ⭐⭐⭐⭐⭐

隔离级别脏读不可重复读幻读
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ标准层面仍需注意
SERIALIZABLE

说明:

✅ = 可以防止

❌ = 可能发生

对于 MySQL InnoDB:

REPEATABLE_READ

还结合:

MVCC

锁机制

进一步处理幻读问题。


十四、Spring 怎么设置隔离级别? ⭐⭐⭐⭐⭐

使用:

@Transactional(
    isolation = Isolation.READ_COMMITTED
)
public void createOrder() {

}

例如:

@Transactional(
    isolation = Isolation.REPEATABLE_READ
)
public void createOrder() {

}

Spring 提供:

Isolation.DEFAULT

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED

Isolation.REPEATABLE_READ

Isolation.SERIALIZABLE

十五、Isolation.DEFAULT 是什么? ⭐⭐⭐⭐⭐

Spring:

@Transactional(
    isolation = Isolation.DEFAULT
)

默认就是:

Isolation.DEFAULT

意思不是:

Spring规定了一个固定隔离级别

而是:

使用底层数据库 / 事务资源的默认隔离级别。

例如:

MySQL InnoDB 默认通常是:

REPEATABLE READ

所以:

Spring DEFAULT
        ↓
使用数据库默认
        ↓
MySQL InnoDB
        ↓
REPEATABLE READ

十六、Propagation 和 Isolation 区别 ⭐⭐⭐⭐⭐

这个一定要分清楚。

你前面学过:

@Transactional(
    propagation = Propagation.REQUIRED
)

Propagation:

解决:

一个事务方法调用另一个事务方法时,事务怎么处理。

例如:

A调用B

↓

B加入A?

还是

B创建新事务?

Isolation:

@Transactional(
    isolation = Isolation.READ_COMMITTED
)

解决:

多个事务并发执行时,事务之间的数据可见性和隔离程度。


所以:

Propagation

=

事务怎么传
Isolation

=

事务之间怎么隔离

十七、举个完整例子

例如:

@Transactional(
    propagation = Propagation.REQUIRES_NEW,
    isolation = Isolation.READ_COMMITTED
)
public void saveLog() {

}

这里:

propagation

决定:

这个方法使用哪个事务?

因为:

REQUIRES_NEW

所以:

创建新事务

而:

isolation

决定:

这个新事务和其他并发事务之间

能看到什么数据?

因为:

READ_COMMITTED

所以:

只能读取别人已经提交的数据

两个参数:

解决的是完全不同的问题。


十八、MVCC 是什么? ⭐⭐⭐⭐

MVCC:

Multi-Version Concurrency Control

中文:

多版本并发控制。

现在不用深入源码。

先理解:

数据库为了:

提高并发能力

不会所有读取都简单粗暴地:

加锁

而是可以保存:

数据的不同版本

让不同事务:

根据自己的:

Read View

读取合适的数据版本。


可以粗略理解:

数据库中:

张三 = 1000

事务B修改:

张三 = 500

↓

数据库保留版本信息

事务A:

根据自己的读取视图:

可能仍然看到1000

其他事务:

根据自己的读取视图:

可能看到500

所以:

MVCC

↓

让读写并发能力更高

后面如果专门学 MySQL:

再详细学:

undo log

Read View

版本链

快照读

当前读

今天先知道:

MySQL InnoDB 的可重复读和 MVCC 密切相关。


十九、快照读和当前读

这个今天先有印象。

普通查询:

SELECT *
FROM user
WHERE id = 1;

通常属于:

快照读

会利用:

MVCC

读取合适的数据版本。


如果:

SELECT *
FROM user
WHERE id = 1
FOR UPDATE;

属于:

当前读

它需要读取:

当前最新的数据

并涉及:

加锁

所以 MySQL 的:

幻读

MVCC

间隙锁

Next-Key Lock

这些问题:

都和:

快照读

当前读

有关。

这部分以后放到 MySQL 专门学。


二十、实际工作怎么选隔离级别?

一般:

不要没事就手动修改隔离级别。

大多数项目:

使用数据库默认隔离级别

就可以。

如果真的出现:

并发一致性要求

性能要求

特殊业务场景

再根据实际情况:

选择隔离级别和锁方案。


🎤 面试回答(30秒)

数据库事务主要有四种标准隔离级别:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE。

它们主要用于解决并发事务中的脏读、不可重复读和幻读问题。

脏读是读取到其他事务未提交的数据;不可重复读是同一个事务中读取同一行数据前后结果不同;幻读是同一个事务中两次条件查询的结果集发生变化。

Spring 可以通过 @Transactional 的 isolation 属性设置隔离级别,默认 Isolation.DEFAULT 表示使用底层数据库的默认隔离级别。MySQL InnoDB 默认通常是 REPEATABLE READ,并结合 MVCC 和锁机制处理并发读取问题。


🎯 面试追问

Q1:什么是脏读?

答:

读取到了其他事务还没有提交的数据。

Q2:什么是不可重复读?

答:

同一个事务中

同一行数据

前后两次读取结果不同。

Q3:什么是幻读?

答:

同一个事务中

两次相同条件查询

符合条件的数据行发生变化。

Q4:不可重复读和幻读有什么区别?

答:

不可重复读:

同一行的数据内容变了


幻读:

符合条件的数据行发生变化

Q5:四种隔离级别是什么?

答:

READ_UNCOMMITTED

READ_COMMITTED

REPEATABLE_READ

SERIALIZABLE

Q6:MySQL InnoDB 默认隔离级别是什么?

答:

REPEATABLE READ

Q7:Spring 默认隔离级别是什么?

答:

Isolation.DEFAULT

表示:

使用底层数据库默认隔离级别

Q8:Propagation 和 Isolation 有什么区别?

答:

Propagation

↓

解决事务怎么传播
Isolation

↓

解决并发事务之间怎么隔离

Q9:什么是 MVCC?

答:

多版本并发控制。数据库通过维护数据版本和读取视图,让不同事务读取合适的数据版本,从而提高读写并发能力。


Q10:什么是快照读?

答:

普通 SELECT:

SELECT *
FROM user;

通常属于:

快照读

主要利用:

MVCC

读取合适的数据版本。


Q11:什么是当前读?

答:

例如:

SELECT *
FROM user
WHERE id = 1
FOR UPDATE;

需要读取:

当前最新数据

并进行相应加锁。


⚠️ 工作踩坑

1. 把事务传播和隔离级别搞混

错误:

REQUIRED

REPEATABLE_READ

不是一类东西。

正确:

REQUIRED

↓

Propagation
↓

事务怎么传
REPEATABLE_READ

↓

Isolation
↓

事务之间怎么隔离

2. 认为隔离级别越高越好

错误。

隔离越强

↓

并发限制通常越多

↓

性能可能下降

应该:

根据业务一致性要求选择。


3. 简单认为 MySQL RR 一定有幻读

不够准确。

MySQL InnoDB 在:

REPEATABLE READ

下结合:

MVCC

锁机制

对幻读问题进行了进一步处理。


4. 随便修改数据库隔离级别

不要因为:

SERIALIZABLE最安全

就直接使用最高隔离级别。

可能导致:

锁等待增加

并发能力下降

性能下降

大多数业务:

先使用数据库默认配置。


❓ 自测

  1. 什么是事务隔离级别?

  2. 为什么需要事务隔离?

  3. 什么是脏读?

  4. 什么是不可重复读?

  5. 什么是幻读?

  6. 不可重复读和幻读有什么区别?

  7. 四种标准事务隔离级别分别是什么?

  8. READ_UNCOMMITTED 有什么问题?

  9. READ_COMMITTED 能解决什么问题?

  10. REPEATABLE_READ 能解决什么问题?

  11. SERIALIZABLE 有什么特点?

  12. MySQL InnoDB 默认隔离级别是什么?

  13. Spring 的 Isolation.DEFAULT 是什么意思?

  14. Propagation 和 Isolation 有什么区别?

  15. MVCC 是什么?

  16. MVCC 为什么可以提高并发能力?

  17. 什么是快照读?

  18. 什么是当前读?

  19. SELECT ... FOR UPDATE 属于什么读?

  20. 为什么不能简单认为隔离级别越高越好?


评论