我正在使用Spring Boot在Java中实现一个REST API。我使用了嵌入式内存数据库H2几个星期,但在某些时候我发现事务隔离有问题。Spring JPA + MySQL和死锁
更确切地说,我有一张表,我需要跟踪“重复”记录。重复只是一个记录,这个记录对于表格列的定义良好的子集是相同的。所以,基本上,当我插入新记录时,我首先检查它是否是重复的,并相应地标记它。一个布尔列“重复”用于此目的。
例如,假设B和C是我检查的列以定义重复项。这是一个有效的状态:
| A | B | C | duplicate | | - | - | - | --------- | | x | y | z | false | | z | y | z | true | | x | y | y | false | | x | y | y | true | | y | y | y | true |
,而这是不一个有效状态:
| A | B | C | duplicate | | - | - | - | --------- | | x | y | z | false | | z | y | z | true | | x | y | y | false | | x | y | y | true | | y | y | y | false |
...因为行3和行5对两个B相同的值和C,因此必须将其中的一个标记为重复。
换句话说,我的要求是将任何恰好已经使用过的值标记为重复。对于给定的一组值,只有一行将被允许具有duplicate == false
。
但是,我的基于Spring的实现没有按预期工作。例如,插入具有相同值的100行应该导致99个重复项,并且只有一个非重复项。但是当我试图并行执行这些插入时,没有检测到很多重复项。
我尝试了几个修补程序,并且在某些时候我开始认为H2没有正确地实现SERIALIZABLE隔离级别。我创建了一个小应用程序进行论证:
@RestController
public class NewFooCtrl {
@Autowired
private FooRepo repo;
@RequestMapping(value = "/foo", method = RequestMethod.POST)
@Transactional(isolation = Isolation.SERIALIZABLE)
public void newFoo(@RequestBody Foo foo) {
List<Foo> foos = repo.findByBar(foo.getBar());
if (foos.isEmpty()) foo.setDuplicate(false);
else foo.setDuplicate(true);
repo.save(foo);
}
}
注:我省略了明显的代码,如模型和信息库。 Foo
模型具有标识符(类型UUID)bar
属性(字符串类型)和duplicate
属性(类型布尔值)。重复检查基于bar
属性。
随着H2我有很多错过重复(通常10%)。使用MySQL我总是有正确的结果(即标记为重复的行数为,正好为 N - 1,N为插入行数)。唯一的问题是只有一小部分插入成功(最多从1%到30%)。
我得到了大量死锁相关的异常。这是为什么?这样简单的代码怎么会导致死锁。我的意思是,这只是一个选择,然后是插入。
有什么建议吗?
您使用的H2和商店引擎的版本是什么? – fg78nc
我不知道如何检查它。我发现了一些MySQL代码来查询给定表的引擎,但它似乎没有在H2上工作。至于版本,我在我的'pom.xml'中列出了依赖项,没有版本号,所以也许它是最新的(?)。 –
请尝试添加到数据库URL'LOCK_MODE = 1; MVCC = TRUE;' – fg78nc