【发布时间】:2018-07-03 09:22:34
【问题描述】:
我正在尝试使用parallelStream() 模拟分布式应用程序,在数据库上写入,其中条目组合应该是唯一的。但是,我尝试了 @Transactional 和 @Lock 的几个选项,但似乎都没有。
这是代码的一部分,应该可以清楚地说明问题:
在AtomicDbService:
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public TestEntity atomicInsert(TestEntity testEntity) {
TestEntityParent testEntityParent = testEntityParentRepository
.findByStringTwo(testEntity.getTestEntityParent().getStringTwo())
.orElseGet(() -> testEntityParentRepository.save(TestEntityParent.builder()
.stringTwo(testEntity.getTestEntityParent().getStringTwo())
.build()));
return testEnityRepository.findByStringAndTestEntityParentStringTwo(
testEntity.getString(), testEntity.getTestEntityParent().getStringTwo()
).orElseGet(() -> testEnityRepository
.save(
TestEntity.builder()
.string(testEntity.getString())
.testEntityParent(testEntityParent)
.build()
)
);
}
测试:
@Test
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void testOperationsParallelStream() {
List<Integer> list = IntStream.range(0, 3).boxed().collect(Collectors.toList());
list.parallelStream().forEach(lala -> atomicDbService.atomicInsert(testEntity));
System.out.println(testEnityRepository.findAll());
}
作为输出我得到例如:
[TestEntity(id=4, string=test, testEntityParent=TestEntityParent(id=3, stringTwo=testTwo)), TestEntity(id=5, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo))]
但实际上应该只有一个结果。更多的线程当然会导致异常。
【问题讨论】:
-
您没有说明您使用的数据库。该隔离模式是否有效(或如何工作)因数据库而异。
-
目前我使用嵌入式 H2 进行测试 (
runtime('com.h2database:h2')。在生产中我们目前使用 MySQL,但将来可能会切换。 -
您似乎希望在数据库级别而不是在应用程序级别保持数据一致性,因此这不是线程安全的。当您有多个应用程序(微服务)实例正在运行时,线程安全不适用。在数据库级别,您需要应用某种形式的锁定:要么是悲观的——你需要在事务开始时锁定一行或一个表——要么是乐观的——你会定义一些会导致事务回滚的约束如果他们被违反了。
-
感谢您的评论。这正是我想要做的。这里我没提,但是save方法注释为悲观写。然而这并没有改变任何东西,据我所知,
isolation.SERIALIZABLE应该做同样的事情吗?
标签: java spring concurrency spring-data-jpa thread-safety