问题描述

在做项目的过程中,由于写SQL太过随意,一不小心就抛了一个死锁异常,如下:

[java] view plaincopyprint?
  1. com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
  2. at sun.reflect.GeneratedConstructorAccessor247.newInstance(Unknown Source)
  3. at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
  4. at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
  5. at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
  6. at com.mysql.jdbc.Util.getInstance(Util.java:381)
  7. at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1045)
  8. at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
  9. at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
  10. at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
  11. at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
  12. at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
  13. at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
  14. at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
  15. at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
  16. at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
  17. at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)

表结构如下:

[sql] view plaincopyprint?
  1. CREATE TABLE `user_item` (
  2. `id` BIGINT(20) NOT NULL,
  3. `user_id` BIGINT(20) NOT NULL,
  4. `item_id` BIGINT(20) NOT NULL,
  5. `status` TINYINT(4) NOT NULL,
  6. PRIMARY KEY (`id`),
  7. KEY `idx_1` (`user_id`,`item_id`,`status`)
  8. ) ENGINE=INNODB DEFAULT CHARSET=utf-8

SQL语句如下:

[sql] view plaincopyprint?
  1. update user_item set status=1 where user_id=? and item_id=?

原因分析

mysql的事务支持与存储引擎有关,MyISAM不支持事务,INNODB支持事务,更新时采用的是行级锁。这里采用的是INNODB做存储引擎,意味着会将update语句做为一个事务来处理。前面提到行级锁必须建立在索引的基础,这条更新语句用到了索引idx_1,所以这里肯定会加上行级锁。

行级锁并不是直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,mysql会锁住主键索引;如果一条语句操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引。

这个update语句会执行以下步骤:

1、由于用到了非主键索引,首先需要获取idx_1上的行级锁

2、紧接着根据主键进行更新,所以需要获取主键上的行级锁;

3、更新完毕后,提交,并释放所有锁。

如果在步骤1和2之间突然插入一条语句:update user_item .....where id=? and user_id=?,这条语句会先锁住主键索引,然后锁住idx_1。

蛋疼的情况出现了,一条语句获取了idx_1上的锁,等待主键索引上的锁;另一条语句获取了主键上的锁,等待idx_1上的锁,这样就出现了死锁。

解决方案

1、先获取需要更新的记录的主键

[sql] view plaincopyprint?
  1. select id from user_item where user_id=? and item_id=?

2、逐条更新

[java] view plaincopyprint?
  1. for (Long id : idList) {
  2. userItemDAO.updateStatus(id, userId, 1);
  3. }
[sql] view plaincopyprint?
  1. update user_item set status=? where id=? and user_id=?

这样貌似解决了,都是对单条进行操作,都是先获取主键上的锁,再获取idx_1上的锁。

不过这个解决方案与先前的更新语句不一样,先前的更新语句对所有记录的更新在一个事务中,采用循环更新后并不在同一个事务中,所以在for循环外面还得开一个事务。

[java] view plaincopyprint?
  1. Exception e = (Exception)getDbfeelTransactionTemplate().execute(new TransactionCallback() {
  2. public Object doInTransaction(TransactionStatus status) {
  3. try {
  4. for(Long id:idList) {
  5. <span style="white-space:pre">  </span>userItemDAO.updateStatus(id,userId,1)
  6. }
  7. return null;
  8. }catch(DAOException e) {
  9. status.setRollbackOnly();
  10. return e;
  11. }
  12. catch (Exception e) {
  13. status.setRollbackOnly();
  14. return e;
  15. }
  16. }
  17. });

小结:在采用INNODB的MySQL中,更新操作默认会加行级锁,行级锁是基于索引的,在分析死锁之前需要查询一下mysql的执行计划,看看是否用到了索引,用到了哪个索引,对于没有用索引的操作会采用表级锁。如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁,否则加锁顺序相反。在并发度高的应用中,批量更新一定要带上记录的主键,优先获取主键上的锁,这样可以减少死锁的发生。

原文:http://blog.csdn.net/aesop_wubo/article/details/8286215

转载于:https://www.cnblogs.com/davidwang456/p/3998361.html

MySQL批量更新死锁案例分析--转载相关推荐

  1. MySql批量更新死锁案例分析

    http://blog.csdn.net/aesop_wubo/article/details/8286215 问题描述 在做项目的过程中,由于写SQL太过随意,一不小心就抛了一个死锁异常,如下: 表 ...

  2. mysql批量插入死锁问题分析(正序VS逆序)

    2019独角兽企业重金招聘Python工程师标准>>> https://dev.mysql.com/doc/refman/5.5/en/innodb-locking.html#inn ...

  3. mysql 批量更新

    mysql更新语句很简单,更新一条数据的某个字段,一般这样写: 复制代码代码如下: UPDATE mytable SET myfield = 'value' WHERE other_field = ' ...

  4. mysql批量更新,批量插入之replace语句/insert into... on duplicate key update语句

    mysql批量更新/插入数据有以下方法,使用这些方法批量插入数据/更新数据的时候就不用在代码层次增加判断数据是否已存在的逻辑了. 1:replace语句 2: insert into... on du ...

  5. mysql 批量更新数据 备份_mysql 批量更新与批量更新多条记录的不同值实现方法...

    批量更新 mysql更新语句很简单,更新一条数据的某个字段,一般这样写: UPDATE mytable SET myfield = 'value' WHERE other_field = 'other ...

  6. 大批量更新数据mysql批量更新的四种方法

    转载一篇大批量更新mysql数据的办法,为了便于阅读根据原文做了缩减. mysql 批量更新如果一条条去更新效率是相当的慢, 循环一条一条的更新记录,一条记录update一次,这样性能很差,也很容易造 ...

  7. dapper mysql 批量_MySQL数据库之c#mysql批量更新的两种方法

    本文主要向大家介绍了MySQL数据库之c#mysql批量更新的两种方法 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 总体而言update 更新上传速度还是慢. 1:  简单的 ...

  8. mybatis之(Oracle,MySql)批量更新

    Oracle和MySql批量更新配置文件不太一样. Oracle: <!-- 对应关系批量更新 --><update id="hotelCusRelUpdate" ...

  9. mysql循环更新数据_大批量更新数据mysql批量更新的四种方法

    mysql 批量更新如果一条条去更新效率是相当的慢, 循环一条一条的更新记录,一条记录update一次,这样性能很差,也很容易造成阻塞. mysql 批量更新共有以下四种办法 1..replace i ...

最新文章

  1. [译] Don’t call me, I’ll call you:使用 Redux-Saga 管理 React 应用中的异步 action (上)...
  2. 比特币现金(BCH)是世界上最好货币的十大理由
  3. 内存的静态分配和动态分配的区别【转】 静态分配内存与动态分配内存的区别
  4. 趣谈设计模式 | 职责链模式(ChainOfResposibility):请求的转发处理
  5. org.junit.runners.model.InvalidTestClassError: Invalid test class ‘‘: 1. No runnable methods
  6. ppk on javascript 笔记(五)
  7. Java代码中方法的特殊修饰符/修饰词/关键字
  8. nginx----linux安装
  9. 【C++快速入门】基础语法篇
  10. 算法应用-百钱买百鸡
  11. vb与数据库(一)之迟到的耿建玲视频总结
  12. 使用Websocket框架之GatewayWorker开发电商平台买家与卖家实时通讯
  13. C#技术点--修改系统时间
  14. Python 基础变量声明
  15. 学习.NET ,提升.NET技能,这些公众号得关注
  16. (转)360和腾讯之争,哪个比较痛?
  17. 计算机英语教学设计反思,英语教学设计与反思
  18. linux域名伪装,基于 Nginx 的 v2+websocket+tls 域名伪装
  19. Apache Log4j2 漏洞解决办法
  20. java 横版游戏开发_用MyEclipse的Java Project开发仿DNF横版格斗游戏

热门文章

  1. 简约代码表白_JS实现520 表白简单代码
  2. dicom多帧转换_Python解析多帧dicom数据详解
  3. android导航条高度修改,Android中修改TabLayout底部导航条Indicator长短的方法
  4. pyqt5能否用于鸿蒙系统,PyQt显示来自opencv的视频流
  5. jasp报错_JSP报错!成功解决问题200+追加分数!
  6. 计算机网络 tcp 阻塞,读书笔记:计算机网络第7章:阻塞控制
  7. java encodedurl_Java ParseUtil.fileToEncodedURL方法代码示例
  8. Second Week: Git与Github的使用
  9. 凯利公式判定持仓比例
  10. python与其他编程语言对比优点_Python编程不同于其他编程语言的优点