主题
Spring 事务传播行为和数据库隔离级别应该怎样选择?
题型:追问 · 传播行为决定调用链怎样共享事务,隔离级别决定并发事务怎样观察数据。
建议回答
传播行为先按业务原子边界选择。REQUIRED 适合大多数“有事务就加入、没有就新建”的场景;REQUIRES_NEW 会挂起外层事务并新开事务,适合确实需要独立提交的记录,但会额外占用连接,并可能让内外事务对锁和数据的预期变复杂;NESTED 通常依赖保存点,只能在同一物理事务内局部回滚,能力取决于事务管理器和数据库。其他传播行为更多用于显式约束是否允许处于事务中。
隔离级别要结合数据库实现和业务不变量选择。读已提交能避免脏读,但同一事务两次查询结果可能变化;可重复读、快照读和串行化在不同数据库上的具体锁与 MVCC 行为不完全一致。即使隔离级别较高,也不能笼统认为所有写偏差、幻读和业务超卖都会自动消失,关键更新仍可能需要条件更新、唯一约束、悲观锁或乐观锁。
事务还应尽量短,把参数校验、耗时计算和远程调用移出持有数据库连接与锁的区间。若必须跨边界协调,要明确采用 Outbox、Saga、可靠消息或补偿等最终一致方案,而不是扩大本地事务想象边界。
方案边界
REQUIRES_NEW的独立提交意味着外层回滚后它可能仍然保留,业务上必须接受这种结果。readOnly = true更多是给框架和数据库的提示,不能当成强安全控制。- 隔离级别越高通常并发代价越大,但实际行为要以所用数据库和 SQL 为准。
进一步追问
- 为什么同一个类内部调用事务方法可能不生效?
- 事务提交成功后再发送消息,怎样避免“数据库成功、消息失败”?
易错点
- 不要把
NESTED和REQUIRES_NEW都说成独立物理事务。 - 不要只背隔离级别定义,必须结合数据库 MVCC、锁行为和业务写入方式。
- 不要在长事务中调用不可控的远程接口,否则会放大锁等待、连接占用和结果未知问题。