Spring 事务隔离级
Spring 事务隔离级别本质是复用数据库的事务隔离级别,它定义了多个并发事务之间的隔离程度,核心解决的是并发事务带来的脏读、不可重复读、幻读等问题。下面我会从基础概念、Spring 隔离级别定义、具体场景、使用方式四个维度,帮你彻底理解这个知识点。
一、先搞懂:并发事务的3个核心问题
在讲隔离级别前,先明确隔离级别要解决的问题(按严重程度排序):
| 问题 | 定义 |
|---|---|
| 脏读(Dirty) | 事务A读取了事务B未提交的数据,B后续回滚,A读到了“无效”数据。 |
| 不可重复读 | 事务A多次读取同一数据,期间事务B修改并提交了该数据,A读取结果不一致。 |
| 幻读(Phantom) | 事务A按条件查询数据,期间事务B插入/删除并提交了符合条件的数据,A再次查询结果条数变化。 |
二、Spring 事务隔离级别(5种)
Spring 在 TransactionDefinition 接口中定义了5种隔离级别,对应数据库的4种标准隔离级别 + 1个“默认”级别(复用数据库默认值,如 MySQL 默认是 REPEATABLE_READ)。
| Spring 隔离级别常量 | 对应数据库级别 | 解决的问题 | 允许的问题 | 性能 |
|---|---|---|---|---|
ISOLATION_DEFAULT |
数据库默认 | 跟随数据库默认配置 | 跟随数据库默认 | 中等 |
ISOLATION_READ_UNCOMMITTED |
读未提交 | 无(最低隔离级别) | 脏读、不可重复读、幻读 | 最高 |
ISOLATION_READ_COMMITTED |
读已提交 | 脏读 | 不可重复读、幻读 | 中高 |
ISOLATION_REPEATABLE_READ |
可重复读 | 脏读、不可重复读 | 幻读(MySQL 已解决) | 中等 |
ISOLATION_SERIALIZABLE |
串行化 | 所有问题(最高隔离级别) | 无(事务串行执行) | 最低 |
各隔离级别详解
-
ISOLATION_DEFAULT(默认)
- 作用:Spring 不指定具体隔离级别,直接使用数据库的默认隔离级别(MySQL 默认
REPEATABLE_READ,Oracle 默认READ_COMMITTED)。 - 建议:日常开发优先用这个,除非有明确的隔离级别需求。
- 作用:Spring 不指定具体隔离级别,直接使用数据库的默认隔离级别(MySQL 默认
-
ISOLATION_READ_UNCOMMITTED(读未提交)
- 规则:允许读取其他事务未提交的数据。
- 示例:事务B修改了一条数据但未提交,事务A能读到这个未提交的修改值;如果B回滚,A就读到了“脏数据”。
- 适用场景:极少使用,仅适用于对数据一致性要求极低、追求极致性能的场景(如日志统计)。
-
ISOLATION_READ_COMMITTED(读已提交)
- 规则:只能读取其他事务已提交的数据,解决脏读。
- 示例:事务B修改数据并提交后,事务A才能读到修改后的值;但A多次读取同一数据时,B可能多次修改提交,导致A“不可重复读”。
- 适用场景:大部分业务系统的默认选择(如 Oracle 原生默认),平衡一致性和性能。
-
ISOLATION_REPEATABLE_READ(可重复读)
- 规则:事务A在整个执行期间,多次读取同一数据的结果始终一致,解决脏读、不可重复读。
- 示例:事务A开始后,无论事务B是否修改并提交数据,A多次读取同一数据都是同一个值;但B插入新数据并提交后,A按条件查询会出现“幻读”(多了一条数据)。
- 补充:MySQL 的 InnoDB 引擎通过间隙锁额外解决了幻读问题,所以 MySQL 的
REPEATABLE_READ实际解决了所有3个问题。 - 适用场景:MySQL 原生默认,适合对数据一致性要求较高的场景(如金融、电商订单)。
-
ISOLATION_SERIALIZABLE(串行化)
- 规则:所有事务串行执行(一个执行完再执行下一个),完全隔离。
- 示例:事务A执行期间,其他事务无法修改/插入A涉及的数据,彻底解决所有并发问题。
- 缺点:性能极差,会导致大量事务等待、超时,甚至死锁。
- 适用场景:极少使用,仅适用于数据一致性要求极高、并发量极低的场景(如财务对账)。
三、Spring 中如何设置隔离级别
通过 @Transactional 的 isolation 属性指定,下面是具体示例:
1. 基础用法
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
// 设置隔离级别为读已提交
@Transactional(isolation = Isolation.READ_COMMITTED)
public void createOrder(Order order) {
// 1. 检查库存(避免脏读)
int stock = orderMapper.checkStock(order.getProductId());
if (stock < order.getNum()) {
throw new RuntimeException("库存不足");
}
// 2. 创建订单
orderMapper.insert(order);
// 3. 扣减库存
orderMapper.reduceStock(order.getProductId(), order.getNum());
}
// 使用数据库默认隔离级别(推荐)
@Transactional
public void updateOrderStatus(Long orderId, Integer status) {
orderMapper.updateStatus(orderId, status);
}
}
2. 结合传播行为使用
// 高一致性场景:串行化 + 必须有事务
@Transactional(
isolation = Isolation.SERIALIZABLE,
propagation = Propagation.REQUIRED,
rollbackFor = Exception.class
)
public void settleAccount(Long userId) {
// 财务对账逻辑,要求绝对一致
}
四、注意事项
- 隔离级别依赖数据库支持:Spring 只是传递隔离级别给数据库,最终由数据库实现(如 MySQL 支持所有级别,SQLite 仅支持串行化)。
- 隔离级别越高,性能越差:级别越高,数据库加锁范围越大、时间越长,并发能力越低,需根据业务平衡一致性和性能。
- 避免滥用串行化:除非是核心财务场景,否则不要用
SERIALIZABLE,优先用READ_COMMITTED或REPEATABLE_READ。 - 脏读/不可重复读不一定是问题:部分业务场景(如实时统计)允许短暂的不一致,可降低隔离级别提升性能。
总结
- Spring 事务隔离级别复用数据库标准,核心解决并发事务的脏读、不可重复读、幻读问题,共5种级别(默认/读未提交/读已提交/可重复读/串行化);
- 日常开发优先用
ISOLATION_DEFAULT(跟随数据库),大部分业务用READ_COMMITTED或REPEATABLE_READ即可; - 隔离级别越高一致性越好,但性能越差,需根据业务场景平衡,避免过度追求高隔离级别导致性能瓶颈。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号