从 READ_UNCOMMITTED 到 SERIALIZABLE:Spring 事务隔离级别全解析

原文来自于:zha-ge.cn/java/119

从 READ_UNCOMMITTED 到 SERIALIZABLE:Spring 事务隔离级别全解析

有一次线上出了个"灵异事件":订单明明查出来是 0 元,用户却支付成功了。运维一脸懵逼:"数据库你是不是给玩坏了?"

我当时心里咯噔一下:这八成是事务隔离级别的问题。

别笑,这种 Bug 在刚入行的时候我也见怪不怪------很多人对事务隔离的理解,基本停留在"读未提交""读已提交"这四个词上。可在真实项目里,隔离级别决定的是系统能不能稳、数据会不会脏、Bug 会不会连锁反应。


那些年我没搞懂的"事务隔离"

还记得第一次用 Spring 事务的时候,我的注解是这样写的:

java 复制代码
@Transactional
public void createOrder() {
    // ...
}

就完事了。

我从没管过"隔离级别"是什么鬼,甚至都不知道 @Transactional 默认的隔离级别是啥。直到那次"0 元订单"事件,我才开始追根溯源,搞懂了数据库的"隔离"到底隔了个啥。


一切的起点:并发带来的三大怪象

多事务并发执行,就可能出现三大"经典怪象":

  • 脏读(Dirty Read):读到了别的事务还没提交的数据。
  • 不可重复读(Non-repeatable Read):同一事务里两次读取同一行,值却变了。
  • 幻读(Phantom Read):同一事务两次查询同条件数据,返回的行数不一样(新增或删除了行)。

事务隔离的本质,就是通过"牺牲一点并发性能"来避免这些怪象。


四大隔离级别全解析

Spring 的事务隔离级别其实就是数据库隔离级别的映射,你可以通过:

java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)

来指定,常用的四种如下:


1. READ_UNCOMMITTED(读未提交)------"能看别人草稿"

  • 脏读 ✅ 可能发生
  • 不可重复读 ✅ 可能发生
  • 幻读 ✅ 可能发生

这个级别下,事务可以读到别人还没提交的数据。比如事务 A 修改了余额,但没提交,事务 B 就能"提前看到"这个新值。 一旦 A 回滚,B 的数据就成了"脏数据"。

👉 优点:性能最好 👉 缺点:数据最不可靠(几乎没人敢在生产用)


2. READ_COMMITTED(读已提交)------"只看成品,不看草稿"

  • 脏读 ❌ 不会发生
  • 不可重复读 ✅ 可能发生
  • 幻读 ✅ 可能发生

大多数主流数据库(如 Oracle、PostgreSQL)默认都是这个级别。 事务只能读到已经提交的数据,脏读没了,但同一个事务里两次读同一行数据可能不同,因为别的事务可能在中途修改了它。

👉 优点:脏读没了,性能也还不错 👉 缺点:数据一致性不够强


3. REPEATABLE_READ(可重复读)------"你看到的永远一样"

  • 脏读 ❌ 不会发生
  • 不可重复读 ❌ 不会发生
  • 幻读 ✅ 可能发生

这个级别保证了:一个事务期间内,读到的数据永远不会变。 MySQL(InnoDB)默认就是这个级别,它通过"MVCC(多版本并发控制)"来实现,保证事务里的多次读取结果一致。

但问题是,行数可能变多,比如:

  • 第一次查余额表有 10 行
  • 事务还没结束时,别的事务插入了一行
  • 第二次查出来 11 行 ------ 这就是"幻读"

👉 优点:读一致性更强 👉 缺点:有可能出现幻读


4. SERIALIZABLE(可串行化)------"你一个我一个,别挤"

  • 脏读 ❌ 不会发生
  • 不可重复读 ❌ 不会发生
  • 幻读 ❌ 不会发生

这是隔离级别的"终极防护":所有事务顺序执行,就像排队买奶茶一样,一个一个来。 问题当然也显而易见------性能可能惨不忍睹,吞吐量骤降。

👉 优点:数据一致性最强 👉 缺点:性能最差(几乎不用在高并发业务里)


踩坑瞬间:隔离级别选错,后果很严重

那次"0 元订单"事件的真相是这样的:

  • 事务 A:扣库存
  • 事务 B:计算价格

A 还没提交,B 就读了库存为 0 的"草稿"数据,直接计算出 0 元订单。 事务回滚之后,库存还在,但价格早已算错。

解决方案? 把隔离级别从 READ_UNCOMMITTED 提升到 READ_COMMITTED,问题立刻消失。

这让我意识到,事务隔离级别不是"数据库的内部细节",而是业务正确性的重要保证。


面试官杀手锏回答

问:四个事务隔离级别分别能防止什么问题?

答题模板(建议直接背下来):

  • READ_UNCOMMITTED:啥都不防,脏读、不可重复读、幻读全有
  • READ_COMMITTED:防脏读,不可重复读和幻读可能有
  • REPEATABLE_READ:防脏读、防不可重复读,幻读可能有
  • SERIALIZABLE:全防,但性能最差

再补一句:"Spring 的隔离级别本质上是映射到底层数据库隔离级别的,具体行为依赖数据库实现,比如 MySQL 的 REPEATABLE_READ 已经用 MVCC 部分防住了幻读。"


写在最后:性能与一致性,永远是拉扯的两端

事务隔离级别就像一条滑竿:

  • 一头是数据一致性
  • 一头是系统性能

你越往一致性那头靠,性能就越低;你越追求性能,风险就越高。

所以,选什么隔离级别从来不是背诵题,而是业务决策。 支付系统、金融结算这类强一致性场景,可能会用到 REPEATABLE_READ 甚至 SERIALIZABLE;而高吞吐的日志分析、推荐系统,READ_COMMITTED 也完全够用。

记住一句话:事务隔离没有"最好",只有"最合适"。

相关推荐
ACGkaka_几秒前
SpringBoot 实战(四十)集成 Statemachine
java·spring boot·后端
Never_Satisfied1 分钟前
在JavaScript中,将包含HTML实体字符的字符串转换为普通字符
开发语言·javascript·html
Java微观世界10 分钟前
Lombok、SpringBoot的底层逻辑:一文读懂Java注解的编译时与运行时处理
后端
考虑考虑13 分钟前
Ubuntu服务器使用 Graphics2D 生成图片时出现文字乱码
运维·服务器·后端
im_AMBER18 分钟前
React 12
前端·javascript·笔记·学习·react.js·前端框架
前端付豪43 分钟前
Vue 中的 JSX:让组件渲染更灵活的正确方式
前端·javascript·vue.js
回家路上绕了弯43 分钟前
内容平台核心工程:最热帖子排行实现与用户互动三元组存储查询
后端·微服务
王元_SmallA1 小时前
服务器公网IP、私网IP、弹性IP是什么?区别与应
java·后端
柠檬味拥抱2 小时前
Java 实现可靠的 WAV 音频拼接:从结构解析到完整可播放的高质量合并方案
后端