繁体   English   中英

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

[英]Differences in behaviour of REQUIRES_NEW and NESTED propagation in Spring transactions

前言

首先:

与Spring事务中的requires_new和嵌套传播之间差异不重复 - 我读了它,但我没有找到我的问题的答案

题:

在阅读了我提到的主题之后,我理解了物理交易数量中传播水平的主要区别:
2 db事务 - 对于外部和内部方法的REQUIRES_NEW
1 db transaction - 用于NESTED for outer和for inner方法。 如果底层数据库不支持保存点,它将无法工作

但是看起来逻辑从我的观点来看是相同的。

如何理解在实践中使用哪个级别? 任何用例了解它? 方便的行为差异的例子?

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

PS2

另外我认为有性能差异:

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

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

对于那种情况,NESTED会更好,因为1长的物理交易而不是100500 + 1

我看到的巨大差异:

在嵌套的情况下:

  • 如果回滚外部事务,则也会回滚嵌套的tra。
  • 可见性 :如果db执行的MVCC很常见,
    • 嵌套的tra看到外部tra的先前更改。
    • 在提交外部之后,嵌套tra的更改将被视为已提交并且对其他tra可见。
  • 性能:请注意,外部事务的工作集由内部事务扩展。 因此更多的锁,MVCC的更多preimage-storage,更长的redo-log-entry。

在requires_new的情况下:

  • 如果外部事务被回滚,则在外部tra的回滚的情况下,不会回滚内部tra的更改。
  • 可见性 :在MVCC的情况下,这是非常常见的,
    • 内部tra将看不到尚未提交的外部tra所做的更改。
    • 即使在提交外部tra之前,嵌套tra的更改也将在提交此内部tra后立即呈现为对其他tra提交并可见。 较少的锁,但由于许多提交更多的外部操作,重做锁中的记录更多。

性能方面 ,如果其他因素不重要,您可以在交易规模和交易数量之间找到收支平衡点。 如果嵌套比requires_new快,那么问题没有一般的答案。

正如你的例子所示,如果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()

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

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

当您需要对外部和内部更改进行持久化或同时回滚两者时,您可能希望使用嵌套或从属事务。 例如,您需要创建一个新用户(使用“外部”服务)并保存其地址(使用“内部”服务)。 如果您的要求是用户必须拥有地址,那么如果保存用户或地址失败,您希望回滚两个更改。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM