Spring交易中REQUIRES_NEW和NESTED传播行为的差异

时间:2018-10-21 18:08:26

标签: java spring transactions spring-transactions

序言

首先:

它不是Activating a state的副本-我读了它,但没有找到我的问题

的答案

问题:

在阅读了主题之后,我提到我了解到传播级别之间的主要区别在于物理交易:
2个数据库事务-用于REQUIRES_NEW的外部方法和内部方法
1 db事务-用于NESTED的外部方法和内部方法。如果基础数据库不支持保存点,它将无法正常工作

但是,从我的角度来看,如果逻辑上看来,逻辑将是相同的。

如何理解在实践中使用哪个级别?有什么用例可以理解吗?方便的行为差异例子?

PS
我想其他交易差异有一些可见性,因为不同的交易提交时间。

P.S.2

我还认为它们之间存在性能差异:

@Transactional
public void outer(){
    for(int i=0;i<100500;i++){
        inner();
    }   
}

@Transactional
public void inner(){
   //some logic
}

在这种情况下,NESTED会更好,因为需要进行1次长时间的物理交易,而不是100500 + 1

3 个答案:

答案 0 :(得分:3)

在您的示例中,如果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()中没有catch块。


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_NEWNESTED只会保留成功的inner()调用中的更改。尽管关键的区别在于,NESTED的使用仍然存在选择,如果outer()随后发生故障,则可以将其全部丢弃。

正如您所说,另一个因素是可伸缩性-一些数据库可能不了解NESTED传播时父事务的大小。


另外,也许值得一说-尽管我怀疑它只是为了使示例清晰。直接调用this.inner()会绕过Spring事务顾问。需要允许它注入“建议的bean”,以使@Transactional注释在调用之前和之后都可以发挥其魔力-例如, nextAutowiredBean.inner()

答案 1 :(得分:2)

我看到的巨大差异:

如果嵌套:

  • 如果外部事务被回滚,则嵌套事务也将被回滚。
  • 可见性:如果数据库同时执行MVCC,
    • 嵌套tra看到外部tra的先前更改。
    • 在提交外部后,嵌套tra的更改将被视为已提交,并且对其他tra可见。
  • 性能:请注意,内部事务扩展了外部事务的工作集。因此,更多的锁,更多的MVCC前映像存储,更长的重做日志条目。

如果是require_new:

  • 如果回滚外部事务,则在回滚外部tra的情况下不会回滚内部tra的更改。
  • 可见性:如果同时使用MVCC,
    • 内部tra不会看到尚未提交的外部tra所做的更改。
    • 嵌套tra的更改将在提交此内部tra之后甚至在提交外部tra之前立即被提交,并且对其他tra可见。更少的锁,但是由于执行更多的外部操作,因此重做锁中的记录更多。

如果性能,如果其他因素不重要,则可以在交易规模和交易数量之间找到收支平衡。 i.m.O,如果嵌套比require_new快,那么这个问题没有一般性的答案。

答案 2 :(得分:1)

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

例如,您的外部方法可能是在处理包含大量记录的作业,然后调用一个内部方法来保留作业状态(进度,警告和验证错误)。您可能希望内部方法事务是独立的,并且其db更改应立即保存,以便系统的其他部分可以显示进度。如果外部方法遇到异常,则其事务将回滚,而内部方法的事务将不回滚。

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