MySQL批量更新死锁案例分析--转载

时间:2022-12-24 22:25:39

问题描述

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

  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)

表结构如下:

  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语句如下:

  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、先获取需要更新的记录的主键

  1. select id from user_item where user_id=? and item_id=?

2、逐条更新

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

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

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

  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

MySQL批量更新死锁案例分析--转载的更多相关文章

  1. &lbrack;转&rsqb;MySQL批量更新死锁案例分析

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

  2. MySql一个生产死锁案例分析

    接到上级一个生产环境MySQL死锁日志信息文件,需要找出原因并解决问题.我将死锁日志部分贴出如下: 在mysql中使用命令:SHOW ENGINE INNODB STATUS;总能获取到最近一些问题信 ...

  3. MySQL死锁案例分析与解决方案

    MySQL死锁案例分析与解决方案 现象: 数据库查询: SQL语句分析:  mysql. 并发delete同一行记录,偶发死锁.   delete from x_table where id=?   ...

  4. mysql 批量更新与批量更新多条记录的不同值实现方法

    作者: 字体:[增加 减小] 类型:转载 时间:2013-10-02 我要评论 在mysql中批量更新我们可能使用update,replace into来操作,下面小编来给各位同学详细介绍mysql ...

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

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

  6. mysql 批量更新

    bs_user 表,我们叫他 bu表, 字段user_id,len_id, think_wellUser 表,我们简称为tw表,中的user_id ,len_id 其中tw表的user_id 是bu表 ...

  7. 170301、使用Spring AOP实现MySQL数据库读写分离案例分析

    使用Spring AOP实现MySQL数据库读写分离案例分析 原创 2016-12-29 徐刘根 Java后端技术 一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案 ...

  8. Mysql 批量更新update的表与表之间操作

    Mysql 批量更新update的表与表之间操作 一.方法一 使用User2表数据更新User表: update User as a ,User2 as b set a.role_id=b.set_v ...

  9. mysql 批量更新常用操作

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

随机推荐

  1. Android音频系统之AudioFlinger&lpar;四&rpar;

    http://blog.csdn.net/xuesen_lin/article/details/8805096 1.1.1 AudioMixer 每一个MixerThread都有一个唯一对应的Audi ...

  2. 【转载】free查看内存

    http://blog.csdn.net/hylongsuny/article/details/7742995 free 命令相对于top 提供了更简洁的查看系统内存使用情况:$ free       ...

  3. 【转】Android通过JNI调用驱动程序&lpar;完全解析实例&rpar;

    原文网址:http://blog.csdn.net/ok138ok/article/details/6560875 要达到的目的:android系统中,用JAVA写界面程序,调用jni中间库提供的接口 ...

  4. js中的整型都是用double存储的,有时候不精确,如,

    sss=187948800449444864 187948800449444860   好像16位的时候还可以. http://segmentfault.com/a/1190000002608050

  5. 常用的HTTP状态码

    成功的状态码: 200 – 服务器成功返回网页 304 – 未修改 失败的状态码: 404 – 请求的网页不存在 503 – 服务器暂时不可用 500 – 服务器内部错误 下面的不是很常用,记住上面那 ...

  6. 发红包android

    立即春节,写个应景的控件         思路分析 1.红包沿着不同的轨迹由上往下运动 2.当手指捕获到一个红包,红包停止原先的运动,能够随着手指的滑动做跟手操作 3.当手指动作停止后,红包放大 4. ...

  7. 牛客网 PAT 算法历年真题 1003: 数素数 &lpar;20&rpar;

    1003:数素数 (20) 时间限制 1000 ms 内存限制 32768 KB 代码长度限制 100 KB 判断程序 Standard (来自 小小) 题目描述 令Pi表示第i个素数.现任给两个正整 ...

  8. switch语句的功能是否完全可以使用if else多选择结构来代替&quest;如果是,为什么还需要switch结构&quest;

  9. bzoj 2138&colon; stone

    Description 话说Nan在海边等人,预计还要等上M分钟.为了打发时间,他玩起了石子.Nan搬来了N堆石子,编号为1到N,每堆 包含Ai颗石子.每1分钟,Nan会在编号在[Li,Ri]之间的石 ...

  10. ActiveMQ-5&period;13&period;0集群

    ActiveMQ集群介绍 ActiveMQ具有强大和灵活的集群功能,但在使用的过程中会发现很多的缺点,ActiveMQ的集群方式主要由两种:Master-Slave(ActiveMQ5.8版本已不可用 ...