博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MySQL死锁的案例
阅读量:4163 次
发布时间:2019-05-26

本文共 2652 字,大约阅读时间需要 8 分钟。

文章目录

死锁的发生

今天碰到一个MySQL死锁的案例,应用启动时发生了死锁,先看日志吧:

2019-03-18 19:40:24 jdbc.sqltiming [ERROR] 45. PreparedStatement.execute() FAILED!    DELETE FROM NGB_DNSMAP_CHAIN_INFO   WHERE dnsSystemId = '17' and dnsmapId = '1563290923705244' and bakDns' and dnsmapSwitchStrategy = '1' and dnsmapRecoverStrategy = '1' and isClusterDispatch = '1' and isPopDispatch = '0' and isAlarmSwitch = '1'   {
FAILED after 1364 msec}com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.Util.getInstance(Util.java:386) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1066) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4190) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4122)

具体SQL语句如下:

DELETE FROM NGB_DNSMAP_CHAIN_INFO   WHERE dnsSystemId = '17' and dnsmapId = '1563290923705244' and bakDns' and dnsmapSwitchStrategy = '1' and dnsmapRecoverStrategy = '1' and isClusterDispatch = '1' and isPopDispatch = '0' and isAlarmSwitch = '1'

表结构如下:

在这里插入图片描述

  • id:主键
  • dnsSystemId:索引
  • dnsmapId:索引

我们使用的是MySQL5.6版本,InnoDB引擎,默认的事务隔离级别(REPEATABLE-READ)

问题分析

首先这个DELETE是批量操作,但并非多线程操作,而是在同一个线程进行操作的,不存在并发问题,因此可以排除同一语句产生死锁的情况。

那么,势必存在另一个或多个语句在和该DELETE语句竞争锁资源,于是我们找到了一个shell脚本会定期执行如下语句(后续沟通发现该脚本是某次迭代更新使用的,没有进行闭环操作),而且有且只有这么一条语句:

UPDATE ngb_dnsmap_chain_info SET isClusterDispatch=1

在继续分析之前,先了解下MySQL InnoDB引擎的锁机制吧。

  • 表锁

    • 如果查询条件中没有使用到索引字段,则都会获取表锁
    • 即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁
  • 行级锁

    • 如果查询条件中使用到索引字段,则会获取行级锁。
    • 行级锁是针对索引加的锁,不是针对数据记录加的锁,所以虽然是访问不同行的记录,但是如果使用相同的索引键,是会出现锁冲突的。
    • 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁

继续分析,姑且给两个语句定义一个名称吧

A语句:

DELETE FROM NGB_DNSMAP_CHAIN_INFO   WHERE dnsSystemId = '17' and dnsmapId = '1563290923705244' and bakDns' and dnsmapSwitchStrategy = '1' and dnsmapRecoverStrategy = '1' and isClusterDispatch = '1' and isPopDispatch = '0' and isAlarmSwitch = '1'

B语句:

UPDATE ngb_dnsmap_chain_info SET isClusterDispatch=1

A语句要执行,会使用到索引锁,先根据索引锁锁住行数据,再去锁住对应的主键

B语句是全表锁,会修改主键和索引记录,因此会先获取主键锁,再获取索引锁

B获取主键锁等待索引锁释放,A获取索引锁等待主键锁释放,因此发生死锁

解决方案

后续将shell脚本停止,成功启动应用

总结

MySQL内部InnoDB引擎的锁机制远远比上面提到的要复杂,在应用中,避免死锁最好的手段就是按顺序获取锁或者根据主键去修改数据。

关于MySQL锁的说明网上资料非常多,这里列出我参考的一篇文章:

转载地址:http://azpxi.baihongyu.com/

你可能感兴趣的文章
springboot 与 任务(异步任务,邮件任务,定时任务)
查看>>
免费内网穿透工具之HTTP穿透
查看>>
http 请求 405 错误,解决办法
查看>>
MinIO存储在docker中安装及其使用
查看>>
java后端优雅进行参数校验
查看>>
MongoDB介绍以及安装和CRUD操作实例
查看>>
在Nginx/Tengine服务器上安装证书
查看>>
quartz整合springboot实现动态配置任务的CRUD详情操作
查看>>
docker中通过docker-compose安装GitLab中文社区版以及优化设置
查看>>
Mbatis-Plus整合springboot详细学习笔记
查看>>
Mybatis-Plus进阶之扩展插件
查看>>
centos 7 阿里云安装ftp服务以及创建用户
查看>>
业务规范之springboot整合swagger2
查看>>
业务规范之统一验证
查看>>
业务规范之统一返回体
查看>>
业务规范之统一异常处理和统一响应
查看>>
docker安装mongodb开启权限认证以及mongodb数据定时脚本备份与恢复
查看>>
SSH免密登录,实现A服务器免密登录B服务器
查看>>
docker安装mongodb开启权限认证以及mongodb数据定时脚本备份与恢复
查看>>
linux中定时任务,定时删除某天之前的文件
查看>>