在spring应用程序的事务中使用异步

nfg76nw0  于 2021-06-23  发布在  Mysql
关注(0)|答案(2)|浏览(650)

我有一个spring应用程序,它使用 @Transactional 方法,并且在同一方法中,我尝试使用 @Async 它是另一个spring应用程序,它从mysql db读取同一实体并更新redis存储中的值。
现在的问题是,每次我更新实体的一些值时,有时在redis中更新,有时不更新。
当我试着调试时,我发现有时第二个应用程序在从mysql读取实体时会选择旧的值而不是更新的值。
有人能告诉我如何避免这种情况,并确保第二个应用程序总是从mysql中选择该实体的更新值吗?

mnowg1ta

mnowg1ta1#

m的回答。deinum是好的,但是仍然有另一种方法可以实现这一点,根据当前应用程序的状态,这种方法对于您的情况可能更简单。
您可以简单地将对async方法的调用 Package 在一个事件中,该事件将在当前事务提交后处理,以便每次都能正确地从db读取更新的实体。
要做到这一点很简单,让我告诉你:

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

 @Transactional
public void doSomething() {

    // application code here

    // this code will still execute async - but only after the
    // outer transaction that surrounds this lambda is completed.
    executeAfterTransactionCommits(() -> theOtherServiceWithAsyncMethod.doIt());

    // more business logic here in the same transaction
}

private void executeAfterTransactionCommits(Runnable task) {
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        public void afterCommit() {
            task.run();
        }
    });
}

基本上,这里发生的事情是,我们为当前事务回调提供了一个实现,并且我们只覆盖aftercommit方法—那里还有其他方法可能有用,请检查它们。如果您想在其他部分中使用它或只是简单地使方法更具可读性,那么为了避免键入相同的样板代码,我在helper方法中提取了它。

beq87vna

beq87vna2#

解决方案并不是那么难,显然您希望在数据写入数据库后触发并更新。这个 @Transactional 仅在方法完成执行后提交。如果另一个 @Async 方法在方法末尾调用,具体取决于事务可能已提交或未提交的提交(或实际的rest调用)的持续时间。
由于事务之外的某些内容只能看到提交的数据,因此它可能会看到更新的数据(如果已经提交),或者仍然是旧的数据。这还取决于事务的序列化级别,但出于性能原因,通常不希望对数据库使用独占锁。
为了解决这个问题 @Async 方法不应从内部调用 @Transactional 但就在那之后。这样,数据总是被提交,而另一个服务将看到更新的数据。

@Service
public class WrapperService {

    private final TransactionalEntityService service1;
    private final AsyncService service2;

    public WrapperService(TransactionalEntityService service1, AsyncService service2) {
        this.service1=service1;
        this.service2=service2;
    }

    public updateAndSyncEntity(Entity entity) {
       service1.update(entity); // Update in DB first
       service2.sync(entity); // After commit trigger a sync with remote system
    }
}

此服务是非事务性的,因此 service1.update 可以推测,这是 @Transactional 将更新数据库。完成后,您可以触发外部同步。

相关问题