单机多线程模式下,出现死锁,如何解决!

系统在运行的过程中,出现了死锁!通过mysql的日志,找到如下错误

主要看标红的地方,为了解决死锁问题,首先要确认系统里面几个事情才能解决

1. 几个系统概念要明晰

第一:我们的数据库mysql的存储引擎为InnoDB,InnoDB对于更新操作如果采用索引的话,则使用行级锁。否则使用表锁。

第二:X锁为排它锁,S为共享锁。排它锁就是指这个线程获得了排它锁,其他现场全部阻塞。而共享锁也就是读锁,获得了共享锁的线程,可以让其他线程读,但是不能修改,当然还有一些更加细致的锁,比如间隙锁(为了避免幻读,比如采用范围读取where A>10,表里的数据从1-15,记录不仅会锁住10-15,还有锁住10以上的,这是为了避免另外一个事物进行插入操作,如果有插入操作,就得等这个事物处理完毕之后,RR的实现原理,就是因为间隙锁的原因)、记录锁(锁定一条记录)等。

第三:我们系统的mysql的数据库的隔离级别是第三级别,也就是可重复读,这个一般来说降低隔离级别,这个方案是不靠谱。

确认了以上几件事情之后,开始处理死锁。

2. 分析错误日志

通过分析以及结合网上解释来看错误日志,特此感谢所有提供帮助的文章。

因为是两个事务,分成两步查看

*** (1) TRANSACTION:
TRANSACTION 2F03B531, ACTIVE 1.524 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 29 lock struct(s), heap size 6960, 21 row lock(s), undo log entries 22
LOCK BLOCKING MySQL thread id: 1309008844 block 1326009307
MySQL thread id 1326009307, OS thread handle 0x2ad3d6ed5700, query id 194212860 172.17.9.105:45440 xiaopingdai137 Updating
update account set total=total+0.0, use_money=use_money+252.37, no_use_money=no_use_money+0.0, collection=collection+-252.37, repay=repay+0.0 where user_id=21322
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 14885 page no 51 n bits 1192 index `FK_Reference_7` of table `www1212`.`account` trx id 2F03B531 lock_mode X locks rec but not gap waiting

具体含义的解释,可以参考https://blog.csdn.net/noaman_wgs/article/details/82529908

事务1执行的是update语句,并且等待X锁的授权

*** (2) TRANSACTION:
TRANSACTION 2F03B534, ACTIVE 1.188 sec starting index read
mysql tables in use 1, locked 1
35 lock struct(s), heap size 6960, 36 row lock(s), undo log entries 66
MySQL thread id 1309008844, OS thread handle 0x2ad54e944700, query id 194212899 172.17.9.105:51948 xiaopin137 Updating
update account set total=total+0.0, use_money=use_money+10.95, no_use_money=no_use_money+0.0, collection=collection+-10.95, repay=repay+0.0 where user_id=9908
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 14885 page no 51 n bits 1192 index `FK_Reference_7` of table `www1212`.`account` trx id 2F03B534 lock_mode X locks rec but not gap

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 14885 page no 29 n bits 1192 index `FK_Reference_7` of table `www1212`.`account` trx id 2F03B534 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (1)

事务2持有了一个锁,并且也在等待一个锁,这是我看到日志的初始印象。那么开始具体分析,然后找出问题,并且解决它。

 

2.1 分析锁的记录

      按照update语句来看,where条件后面是根据user_id, 并且user_id在数据库里面也是唯一的,为什么会出现锁住这么多条的数据呢。并且user_Id也是索引,InnoDB的话,如果是索引项,是行级锁,如果能唯一确认一条数据,那么应该不会出现死锁。

但是实际的情况是我认为的不一定是对的,只能一点一点查了。

2.1.1首先确认数据库的索引是什么类型的

SHOW INDEX FROM account

具体每一项的解释,参考此篇文章https://blog.csdn.net/javamoo/article/details/70184088

Non_unique的状态为1,说明可以重复,不是唯一索引,也不是主键索引,就是普通索引。

2.1.2确认btree的索引模式是什么

参考此篇文章https://blog.csdn.net/xu_flash/article/details/62216969

表述非常清晰。

2.13最后确认索引以及mysql行级锁之间的关系

参考此篇文章https://blog.csdn.net/summerZBH123/article/details/81224338,写的也是非常到位。

经过以上三个步骤,确认问题

由于update语句采用的是普通索引,并不是唯一索引,那么在进行update语句的时候, 根据InnerDB存储引擎的实现规则,需要先找到user_id对应的主键,加锁,然后找到主键对应的行数据,再次加锁,那么这两步加锁的数据可能不止一行,有可能是多行, 可以从以上标红的锁定的行数看的出来,由于是并发,所以当出现同一时刻执行update的时候,事务1锁住的事物2的要用的数据,事物2锁住了事物1要用的数据,就死锁了。解决办法如下,修改update语句,现根据user_id查询account表中的主键,再根据主键进行更新操作即可。

 

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值