java mysql 死锁,java-Spring JPA MySQL和死锁

我正在研究使用Spring Boot在Java中实现的REST API.我使用了嵌入式内存数据库H2数周,但在某个时候我注意到事务隔离存在问题.

更准确地说,我有一个表,需要在其中跟踪“重复”记录.重复只是一条记录,对于表的列的定义明确的子集而言,它等于另一条记录.因此,基本上,当我插入新记录时,我首先检查它是否重复并相应地标记它.布尔列“ duplicate”用于此目的.

例如,假设B和C是我为了定义重复项检查的列.这是有效状态:

| A | B | C |重复|

| -| -| -| ——— |

| x | y | z |错误

| z | y | z |真实|

| x | y | y |错误

| x | y | y |真实|

| y | y | y |真实|

虽然这不是有效状态:

| A | B | C |重复|

| -| -| -| ——— |

| x | y | z |错误

| z | y | z |真实|

| x | y | y |错误

| x | y | y |真实|

| y | y | y |错误

…因为第3行和第5行的B和C值相同,因此必须将两者之一标记为重复.

换句话说,我的要求是将碰巧已经使用过的任何行标记为重复.给定一组值的仅一行将允许重复== 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 foos = repo.findByBar(foo.getBar());

if (foos.isEmpty()) foo.setDuplicate(false);

else foo.setDuplicate(true);

repo.save(foo);

}

}

注意:我省略了明显的代码,例如模型和存储库. Foo模型具有一个标识符(UUID类型),一个bar属性(String类型)和一个重复属性(boolean类型).重复检查基于bar属性.

使用H2时,我会错过很多重复项(通常为10%).使用MySQL我总是有正确的结果(即标记为重复的行数正好是N-1,其中N是插入的行数).唯一的问题是只有一小部分插入成功(最多从1%到30%).

我遇到了大量与死锁相关的异常.这是为什么?这样简单的代码怎么会导致死锁.我的意思是,这只是选择,然后是插入.

有什么建议吗?

解决方法:

应用程序不应在事务中检查重复的密钥本身.使用唯一索引将其留给数据库引擎,如果发生异常,则捕获异常,然后使用另一个标识符重试.

如果您确实想在应用程序级别解决此问题,则也许应该在打开事务后立即手动锁定表.隔离级别可以自动为您执行此操作,但是会带来较高的性能成本(您可能不希望这样做).

另一个解决方案是使用@Version批注进行乐观锁定,但是那样您将不能保证标识符的唯一性.

很难诊断死锁问题,但是通常在您具有递归事务(在另一个事务中打开一个事务)时出现.检查您的bean @Scope,它们可以创建此类问题.另外,请确保只有一个TransactionManager和一个EntityManager bean.

标签:jpa,deadlock,spring,java,mysql

来源: https://codeday.me/bug/20191111/2018614.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值