事务隔离级别与回滚规则

结论先行:隔离级别解决并发读写下的三个问题——脏读、不可重复读、幻读;Spring 通过 TransactionDefinition 暴露 5 个常量(DEFAULT 交给数据库),底层映射到 Connection.setTransactionIsolation。回滚规则必须背熟:@Transactional 默认只对 RuntimeException 与 Error 回滚,受检异常(checked)默认不回滚,要让受检异常也回滚必须显式 rollbackFor = Exception.class(或 xxx.class)。

一、隔离级别与并发问题

隔离级别脏读不可重复读幻读备注
READ_UNCOMMITTED可能可能可能几乎不用
READ_COMMITTED不会可能可能Oracle 等默认
REPEATABLE_READ不会不会可能MySQL InnoDB 默认;MVCC + 间隙锁基本防住幻读
SERIALIZABLE不会不会不会锁最重、并发最差
  • 脏读:读到别的事务未提交的数据;不可重复读:同一行两次读结果不同(行被改);幻读:同一条件两次查询行数不同(被插入/删除)。

二、代码示例

@Transactional(
    isolation = Isolation.REPEATABLE_READ,   // 显式指定(一般用 DEFAULT 跟随数据库)
    rollbackFor = Exception.class,           // 受检异常也回滚 ← 最常写的配置
    noRollbackFor = BusinessIgnorableException.class  // 个别异常不回滚
)
public void transfer(Account from, Account to, BigDecimal amt) {
    // 扣款 + 入账
}

三、回滚规则要点

  • 触发条件:异常必须从方法传播到事务拦截器——方法内 catch 掉的异常不会触发回滚;
  • 默认回滚:RuntimeException、Error;默认不回滚:受检异常(历史设计:checked 表示调用方须处理的业务预期);
  • 判定依据:回滚标记 rollback-only 由传播链上的拦截器根据异常类型设置;
  • 多数据源/多事务管理器场景:@Transactional(transactionManager = "xxxTransactionManager") 指定。

常见追问 / 记忆点

  • 追问 1:为什么默认不回滚受检异常?答:Spring 沿袭 EJB 时代约定,认为 checked 是"调用方可以处理"的业务预期,不让其轻易毁掉整个事务;工程上为了严谨常统一 rollbackFor = Exception.class。
  • 追问 2:MySQL 默认隔离级别与幻读?答:REPEATABLE_READ,InnoDB 通过 MVCC 保证快照读一致、间隙锁/next-key lock 防幻读,因此实际很少出现幻读。
  • 记忆点:"隔离级别越严越慢:读未提交 → 读已提交 → 可重复读 → 串行化";"运行时异常默认回滚、受检异常默认不滚,想全滚就 rollbackFor=Exception.class"。
笔记加载中…