事务看起来是一个注解或一段 SQL 配置,底层其实依赖数据库能力。Spring 可以帮我们开启、提交和回滚事务,但它不能凭空制造数据库不支持的一致性语义。
ACID 是事务的基本承诺
ACID 分别是原子性、一致性、隔离性和持久性。
原子性表示一组操作要么全部成功,要么全部失败;一致性表示事务前后数据约束不被破坏;隔离性表示并发事务之间互不干扰到约定程度;持久性表示提交后的结果要可靠保存。
真正落到 MySQL InnoDB 里,事务和日志、锁、MVCC、redo log、undo log 都有关。业务开发不一定每天看这些实现,但遇到回滚、脏读、锁等待时必须知道事务不是魔法。
Spring 事务传播解决调用链问题
传播属性关注的是:一个事务方法调用另一个事务方法时,内外层事务怎么关系。
常用的是 REQUIRED,有事务就加入,没有事务就新建。它适合作为默认值,因为同一业务用例里的多个数据库操作通常应该一起提交或一起回滚。
REQUIRES_NEW 会挂起外层事务,新开一个独立事务。内层提交后,即使外层失败,内层也不会跟着回滚。它适合审计日志、操作记录这类“主流程失败也希望保留”的场景,但要谨慎使用。
NESTED 依赖保存点,内层失败可以回滚到保存点,外层可以选择继续或回滚。它不是两个完全独立事务,更像父事务里的分支。
隔离级别解决并发读取问题
隔离级别关注的是多个事务同时读写同一批数据时,会看到什么。
常见问题有三类:
- 脏读:读到了别人还没提交的数据。
- 不可重复读:同一事务里两次读同一行,结果变了。
- 幻读:同一事务里按条件查询,第二次多出了符合条件的新行。
读未提交最宽松,可能脏读;读已提交能避免脏读;可重复读能避免同一行不可重复读;串行化最严格,但并发能力最弱。
隔离级别越高,并发冲突和锁成本通常越高。选型时不能只追求“最安全”,还要看业务对一致性和吞吐量的要求。
`REQUIRES_NEW` 和 `NESTED` 不要混淆
这两个都容易被理解成“新开事务”,但语义不同。
REQUIRES_NEW 是完全独立的事务,外层回滚不影响已经提交的内层。NESTED 是嵌套事务,靠 savepoint 回滚局部,外层最终回滚时,内层也会一起回滚。
如果你的目标是“局部失败不影响主事务,但主事务失败时全部回滚”,可以考虑 NESTED。如果目标是“无论主事务成败都记录一条日志”,才更接近 REQUIRES_NEW。
分布式事务先问能否最终一致
跨服务、跨库之后,本地事务不再覆盖完整业务链路。此时不要第一反应就上强分布式事务。
先判断业务是否可以接受最终一致。如果可以,优先用本地事务加事件、消息队列、补偿、幂等和对账。只有无法接受中间状态,且业务成本值得,才评估 TCC、Saga、Seata 等方案。
实用结论
事务要分三层理解:数据库用日志、锁和 MVCC 保证基础能力;Spring 用传播属性管理调用链;业务用隔离级别、幂等和补偿定义一致性边界。
写事务代码时,最重要的是明确哪些操作必须同生共死,哪些操作可以独立提交,哪些异常应该触发回滚,哪些并发读写可以接受短暂不一致。




