MySQL批量更新死锁案例分析--转载
问题描述
在做项目的过程中,由于写SQL太过随意,一不小心就抛了一个死锁异常,如下:
- com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
- at sun.reflect.GeneratedConstructorAccessor247.newInstance(Unknown Source)
- at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
- at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
- at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
- at com.mysql.jdbc.Util.getInstance(Util.java:381)
- at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1045)
- at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
- at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
- at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
- at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
- at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
- at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
- at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
- at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
- at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
- at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)
表结构如下:
- CREATE TABLE `user_item` (
- `id` BIGINT(20) NOT NULL,
- `user_id` BIGINT(20) NOT NULL,
- `item_id` BIGINT(20) NOT NULL,
- `status` TINYINT(4) NOT NULL,
- PRIMARY KEY (`id`),
- KEY `idx_1` (`user_id`,`item_id`,`status`)
- ) ENGINE=INNODB DEFAULT CHARSET=utf-8
SQL语句如下:
- 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、先获取需要更新的记录的主键
- select id from user_item where user_id=? and item_id=?
2、逐条更新
- for (Long id : idList) {
- userItemDAO.updateStatus(id, userId, 1);
- }
- update user_item set status=? where id=? and user_id=?
这样貌似解决了,都是对单条进行操作,都是先获取主键上的锁,再获取idx_1上的锁。
不过这个解决方案与先前的更新语句不一样,先前的更新语句对所有记录的更新在一个事务中,采用循环更新后并不在同一个事务中,所以在for循环外面还得开一个事务。
- Exception e = (Exception)getDbfeelTransactionTemplate().execute(new TransactionCallback() {
- public Object doInTransaction(TransactionStatus status) {
- try {
- for(Long id:idList) {
- <span style="white-space:pre"> </span>userItemDAO.updateStatus(id,userId,1)
- }
- return null;
- }catch(DAOException e) {
- status.setRollbackOnly();
- return e;
- }
- catch (Exception e) {
- status.setRollbackOnly();
- return e;
- }
- }
- });
小结:在采用INNODB的MySQL中,更新操作默认会加行级锁,行级锁是基于索引的,在分析死锁之前需要查询一下mysql的执行计划,看看是否用到了索引,用到了哪个索引,对于没有用索引的操作会采用表级锁。如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁,否则加锁顺序相反。在并发度高的应用中,批量更新一定要带上记录的主键,优先获取主键上的锁,这样可以减少死锁的发生。
原文:http://blog.csdn.net/aesop_wubo/article/details/8286215
MySQL批量更新死锁案例分析--转载的更多相关文章
- [转]MySQL批量更新死锁案例分析
文章出处:http://blog.csdn.net/aesop_wubo/article/details/8286215 问题描述 在做项目的过程中,由于写SQL太过随意,一不小心就抛了一个死锁异常, ...
- MySql一个生产死锁案例分析
接到上级一个生产环境MySQL死锁日志信息文件,需要找出原因并解决问题.我将死锁日志部分贴出如下: 在mysql中使用命令:SHOW ENGINE INNODB STATUS;总能获取到最近一些问题信 ...
- MySQL死锁案例分析与解决方案
MySQL死锁案例分析与解决方案 现象: 数据库查询: SQL语句分析: mysql. 并发delete同一行记录,偶发死锁. delete from x_table where id=? ...
- mysql 批量更新与批量更新多条记录的不同值实现方法
作者: 字体:[增加 减小] 类型:转载 时间:2013-10-02 我要评论 在mysql中批量更新我们可能使用update,replace into来操作,下面小编来给各位同学详细介绍mysql ...
- mysql 批量更新与批量更新多条记录的不同值实现方法
批量更新 mysql更新语句很简单,更新一条数据的某个字段,一般这样写: 代码如下: UPDATE mytable SET myfield = 'value' WHERE other_field = ...
- mysql 批量更新
bs_user 表,我们叫他 bu表, 字段user_id,len_id, think_wellUser 表,我们简称为tw表,中的user_id ,len_id 其中tw表的user_id 是bu表 ...
- 170301、使用Spring AOP实现MySQL数据库读写分离案例分析
使用Spring AOP实现MySQL数据库读写分离案例分析 原创 2016-12-29 徐刘根 Java后端技术 一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案 ...
- Mysql 批量更新update的表与表之间操作
Mysql 批量更新update的表与表之间操作 一.方法一 使用User2表数据更新User表: update User as a ,User2 as b set a.role_id=b.set_v ...
- mysql 批量更新常用操作
mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下: UPDATE mytable SET myfield = 'value' WHERE other_field = ' ...
随机推荐
- UITableView 的使用总结
确定单元格的位置:首先要知道分区号,在知道行号. UITableView:API文档的总结:1.UITableView的父类是:UIScrollview,所以他是能滚动的,但是只能在竖直方向滚动.2. ...
- firefox 的event事件处理
前几天,在用angularJs实现一个功能,点击后获取event的x,y坐标时,IE9, chrome下功能正常.但是firefox报event 未定义.初始代码如下: html: <div c ...
- Mysql软删除
所谓软删除(Soft Deleting),即在删除数据表中的数据时,并不直接将其从数据表中删除,而是将其标志为删除,即在每张表中设置一个删除字段(如:IsDeleted)等,默认情况下其值为0,及未删 ...
- Fractal_Test
本文由博主(YinaPan)原创,转载请注明出处:http://www.cnblogs.com/YinaPan/p/Fractal_Test.html 参考:http://catlikecoding ...
- Javascript深度克隆一个对象
Javascript中的对像赋值与Java中是一样的,都为引用传递.就是说,在把一个对像赋值给一个变量时,那么这个变量所指向的仍就是原来对 像的地址.那怎么来做呢?答案是“克隆”. 克隆有两种方法:一 ...
- shell每日发邮件
LOGFILE="$fank/"`date +"%Y%m%d"`"data"#每日文件 from="abc@123.com&quo ...
- js 判断url的?后参数是否包含某个字符串
function GetQueryString(name){ var reg=eval("/"+name+"/g"); var r = window. ...
- call和apply
在js中经常会看到call和apply,他们实际上就是用于改变this的上下文 经典例子 function pet(words) { this.words=words; this.speak=func ...
- 深入Java虚拟机读书笔记第一章Java体系结构介绍
第1章 Java体系结构介绍 Java技术核心:Java虚拟机 Java:安全(先天防bug的设计.内存).健壮.平台无关.网络无关(底层结构上,对象序列化和RMI为分布式系统中各个部分共享对象提供了 ...
- table表格cellspacing与cellpadding属性
cellspacing属性 用来指定表格各单元格之间的空隙. cellpadding属性 用来指定单元格内容与单元格边界之间的空白距离的大小. 此属性的参数值也是数字,表示单元格内容与上下边界之间空白 ...