为了账号安全,请及时绑定邮箱和手机立即绑定

Spring 事务中 REQUIRES_NEW 和 NESTED 传播的行为差异

Spring 事务中 REQUIRES_NEW 和 NESTED 传播的行为差异

手掌心 2021-12-01 14:57:23
前言首先:它不是Spring 事务中 requires_new 和嵌套传播之间的差异的重复- 我阅读了它,但我没有找到我的问题的答案题:在阅读我提到的主题后,我明白了物理事务计数中传播级别之间的主要区别:2 db 事务 -REQUIRES_NEW用于外部和内部方法1 db 事务 -NESTED用于外部和内部方法。如果底层数据库不支持保存点,它将不起作用但从我的角度来看,逻辑似乎是一样的。如何理解在实践中使用哪个级别?任何用例来理解它?行为差异的方便例子?PS我想其他事务的差异有一些可见性,因为不同的事务提交时间。PS2另外我想有性能差异:@Transactionalpublic void outer(){    for(int i=0;i<100500;i++){        inner();    }   }@Transactionalpublic void inner(){   //some logic}对于这种情况,NESTED 会更好,因为 1 个长物理交易而不是 100500+1
查看完整描述

3 回答

?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

就您的示例而言,如果inner()有:


@Transactional(propagation=Propagation.REQUIRES_NEW)

public void inner(){

   //some logic

}

然后,如果它在outer()循环中的第二个调用中抛出异常,则第一个调用中的更改将已经提交 - 由其REQUIRES_NEW.


如果inner()有:


@Transactional(propagation=Propagation.NESTED)

public void inner(){

   //some logic

}

然后将回滚第一次调用的更改 - 因为outer().


传播级别inner()真正开始重要的一点是outer()循环是否要处理以下异常inner():


@Transactional

public void outer() {

    for (int i = 0; i < 100500; i++) {

        try {

            inner();

        } catch (Exception ex) {

            // Report and continue

        }

    }

    // Something else that could fail

}

显然,两者REQUIRES_NEW并NESTED只保留从成功改变inner()通话。但关键的区别在于,NESTED如果outer().


正如您所说,另一个因素是可扩展性 - 某些数据库可能不会通过NESTED传播来理解父事务的大小。


此外,这可能值得一提——尽管我怀疑它只是为了让示例更加清晰。this.inner()直接调用是绕过 Spring 事务顾问程序。它需要被允许注入一个“被建议的 bean”,以允许@Transactional注释在调用之前和之后发挥它的魔力——例如nextAutowiredBean.inner()。


查看完整回答
反对 回复 2021-12-01
?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

我看到的最大差异:

在嵌套的情况下:

  • 如果外部事务回滚,则嵌套的 tra 也会回滚。

  • 可见性:如果数据库同时执行非常常见的 MVCC,

    • 嵌套的 tra 会看到外部 tra 的先前更改。

    • 在外部提交后,嵌套 tra 的更改将被提交,并且对其他 tra 可见。

  • 性能:请注意,外部事务的工作集会被内部事务扩展。所以更多的锁,更多的 MVCC 原像存储,更长的重做日志条目。

在 requires_new 的情况下:

  • 如果外层事务回滚,则外层tra回滚的情况下,内层tra的变化不会回滚。

  • 可见性:对于同时非常常见的 MVCC,

    • 内部 tra 不会看到尚未提交的外部 tra 所做的更改。

    • 在这个内部 tra 提交之后,甚至在外部 tra 提交之前,嵌套 tra 的更改将被提交并立即对其他 tra 可见。锁更少,但由于提交更多的外部操作,redo-lock 中的记录更多。

性能方面,如果其他因素不重要,您可以在交易大小和交易数量之间找到收支平衡。如果嵌套的速度比 requires_new 快,那么这个问题没有通用的答案。


查看完整回答
反对 回复 2021-12-01
?
墨色风雨

TA贡献1853条经验 获得超6个赞

如果您的内部逻辑独立于外部逻辑,则使用 Requires_new,如果不使用嵌套。

例如,您的外部方法可能正在处理包含大量记录的作业并调用保持作业状态(进度、警告和验证错误)的内部方法。您希望内部方法事务是独立的,并且它的数据库更改会立即保存,以便系统的其他部分可以显示进度。如果外部方法遇到异常,它的事务会回滚,但内部方法的事务不会。

当您需要将外部和内部更改同时保留或同时回滚时,您可能希望使用嵌套或依赖事务。例如,您需要创建一个新用户(使用“外部”服务)并保存他们的地址(使用“内部”服务)。如果您的要求是用户必须有一个地址,那么如果保存用户或地址失败,您希望这两个更改都回滚。


查看完整回答
反对 回复 2021-12-01
  • 3 回答
  • 0 关注
  • 371 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信