简体   繁体   English

如何在 Spring 中处理并发访问的事务

[英]How to handle transactions with concurrent access in Spring

I have a service with one method:我有一种方法的服务:

    @Service
    public class DefaultTestService implements TestService {
        private static final Logger LOGGER = Logger.getLogger(DefaultTestService.class);
        @Autowired
        private TestRepository testRepository;

        @Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
        @Override
        public void incrementAndGet(Long testModelId) {
            LOGGER.debug("Transaction is active: " + TransactionSynchronizationManager.isActualTransactionActive());
            final TestModel tm = testRepository.findOne(testModelId);
            if (tm != null) {
                LOGGER.debug("Updated " + testModelId + " from value: " + tm.getValue());
                tm.setValue(tm.getValue() + 1);
                testRepository.save(tm);
            } else {
                LOGGER.debug("Saved with id: " + testModelId);
                final TestModel ntm = new TestModel();
                ntm.setId(testModelId);
                testRepository.save(ntm);
            }
       }
    }

And I'm running Gatling with 2 parallel calls config with testModelId = 1L argument.而且我正在使用带有testModelId = 1L参数的 2 个并行调用配置运行 Gatling。 As a result of these calls I'm getting error:由于这些调用,我收到错误:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"

What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是两个调用一次进入了这个方法,并且每个打印的日志

"Saved with id: 1"
"Saved with id: 1"

I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId) until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻止testRepository.findOne(testModelId)行中的一个调用,直到其他调用完成其执行,但正如我从日志中看到的那样,它以不同的方式工作。

So my question is how transaction works in this case when concurrent access appears?所以我的问题是当并发访问出现时,在这种情况下事务如何工作? And how can I handle this case with concurrent access?以及如何通过并发访问处理这种情况?

I have a service with one method:我有一种方法的服务:

    @Service
    public class DefaultTestService implements TestService {
        private static final Logger LOGGER = Logger.getLogger(DefaultTestService.class);
        @Autowired
        private TestRepository testRepository;

        @Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
        @Override
        public void incrementAndGet(Long testModelId) {
            LOGGER.debug("Transaction is active: " + TransactionSynchronizationManager.isActualTransactionActive());
            final TestModel tm = testRepository.findOne(testModelId);
            if (tm != null) {
                LOGGER.debug("Updated " + testModelId + " from value: " + tm.getValue());
                tm.setValue(tm.getValue() + 1);
                testRepository.save(tm);
            } else {
                LOGGER.debug("Saved with id: " + testModelId);
                final TestModel ntm = new TestModel();
                ntm.setId(testModelId);
                testRepository.save(ntm);
            }
       }
    }

And I'm running Gatling with 2 parallel calls config with testModelId = 1L argument.我正在使用带有testModelId = 1L参数的2个并行调用config运行Gatling。 As a result of these calls I'm getting error:这些调用的结果是我得到了错误:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"

What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是,有两个调用一次输入了此方法,每个打印的日志

"Saved with id: 1"
"Saved with id: 1"

I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId) until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻塞对testRepository.findOne(testModelId)的调用,直到其他调用完成执行为止,但是正如我从日志中看到的那样,它以不同的方式工作。

So my question is how transaction works in this case when concurrent access appears?所以我的问题是,当出现并发访问时,事务在这种情况下如何工作? And how can I handle this case with concurrent access?我如何通过并发访问来处理这种情况?

I have a service with one method:我有一种方法的服务:

    @Service
    public class DefaultTestService implements TestService {
        private static final Logger LOGGER = Logger.getLogger(DefaultTestService.class);
        @Autowired
        private TestRepository testRepository;

        @Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
        @Override
        public void incrementAndGet(Long testModelId) {
            LOGGER.debug("Transaction is active: " + TransactionSynchronizationManager.isActualTransactionActive());
            final TestModel tm = testRepository.findOne(testModelId);
            if (tm != null) {
                LOGGER.debug("Updated " + testModelId + " from value: " + tm.getValue());
                tm.setValue(tm.getValue() + 1);
                testRepository.save(tm);
            } else {
                LOGGER.debug("Saved with id: " + testModelId);
                final TestModel ntm = new TestModel();
                ntm.setId(testModelId);
                testRepository.save(ntm);
            }
       }
    }

And I'm running Gatling with 2 parallel calls config with testModelId = 1L argument.我正在使用带有testModelId = 1L参数的2个并行调用config运行Gatling。 As a result of these calls I'm getting error:这些调用的结果是我得到了错误:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"

What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是,有两个调用一次输入了此方法,每个打印的日志

"Saved with id: 1"
"Saved with id: 1"

I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId) until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻塞对testRepository.findOne(testModelId)的调用,直到其他调用完成执行为止,但是正如我从日志中看到的那样,它以不同的方式工作。

So my question is how transaction works in this case when concurrent access appears?所以我的问题是,当出现并发访问时,事务在这种情况下如何工作? And how can I handle this case with concurrent access?我如何通过并发访问来处理这种情况?

You could use optimistic locking, just as shown in this Baeldung guide .您可以使用乐观锁定,正如本Baeldung 指南中所示。 A little summary of what must be done.必须做的小总结。

  1. Adding a bigint (or anything similar) version field to the tables that were modified in the commonly deadlocked transactions (database wise)向在常见死锁事务中修改的表添加一个bigint (or anything similar)版本字段(数据库方面)
  2. Adding the annotation @Version to the JPA model's newly created version field在 JPA 模型新创建的版本字段中添加注解@Version

With a quite simple solution, most of my deadlocked issues disappear.通过一个非常简单的解决方案,我的大部分僵局问题都消失了。 Hope it helps somebody else.希望它可以帮助别人。

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

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