我正在使用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