mysql insert 偶出现Deadlock死锁场景分析

23 篇文章 1 订阅
1 篇文章 0 订阅

有一张表A,先删除数据,如果影响行数为0,则执行INSERT插入数据。很常见的场景,在生产上也跑了很久,没有出现什么问题。但是有一次在测试环境做压测时居然出现了死锁,Deadlock found when trying to get lock; try restarting transaction

因为对mysql锁不熟悉,为什么insert也会死锁,不是一般在update的时候会死锁吗? 很好奇,于是开始寻找原因…

mysql锁是跟数据库设置的隔离级别有关系的,不同的隔离级别,锁也各不相同,只要是为了解决类似脏读、幻读、可重复读的问题。
select @@tx_isolation; – 查看隔离级别

隔离级别是RR(可重复读),我们知道,在此隔离级别下,为了解决幻读的问题,会有存在间隙锁和Next-key lock(行锁 + 间隙锁),即在update、delete语句后会产生间隙锁和Next-key lock,如果在并发下,存在两个事务都事前执行了UPDATE语句(各自持有了gap lock),当INSERT时,要先在插入间隙上获取插入意向锁,由于插入数据的间隙存在冲突,所以会互相等待获取插入意向锁,即相互竞争,最终会导致一方死锁。

这也解释了为什么在测试环境出现死锁。
解决:

  1. 不采用事务,即无事务方式执行,但是如果出现异常则会出现数据不一致的情况。
  2. 调整事务隔离级别为read commit,RC级别不会产生gap lock

由于我们都知道事务的重要性,所以选择第二种方式,将隔离级别改为RC。我们在生产环境一般也就这个级别,所以也解释了为什么在生产没有出现这个问题。

调整事务隔离级别为read commit方式:

方案一:事务隔离级别调整可以通过spring的声明式事务方式来实现,通过注解@Transactional

@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public void calculationEntry(List<CampaignFeedback> campaignFeedbackList, String groovyScript) {
    //todo
}

方案二:调整mysql的默认事务隔离级别(或者mycat的默认事务隔离级别)

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

 如果出现下面错误则继续执行以下语句

SET GLOBAL binlog_format = 'STATEMENT';

SET GLOBAL binlog_format = 'ROW';

SET GLOBAL binlog_format = 'MIXED';

修改mycat的默认事务隔离级别 <property name="txIsolation">2</property>

property name="txIsolation">3</property>
前端连接的初始化事务隔离级别,只在初始化的时候使用,后续会根据客户端传递过来的属性对后端数据库连接进行同步。默认为 REPEATED_READ,设置值为数字默认 3。
READ_UNCOMMITTED = 1;
READ_COMMITTED = 2;
REPEATED_READ = 3;
SERIALIZABLE = 4;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值