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 = ' ...
随机推荐
- C#字符串string的常用使用方法
1--->字符串的声明: 1.string s=new string(char[] arr) //根据一个字符数组声明字符串,即将字符字组转化为字符串. 2.string s=new s ...
- cas sso单点登录系列1_cas-client Filter源码解码(转)
转:http://blog.csdn.net/ae6623/article/details/8841801?utm_source=tuicool&utm_medium=referral /* ...
- java学习——IO流
字符流的由来:其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表.获取对应的文字.在对这个文字进行操作.简单说:字节流+编码表 ---------------------------- ...
- BroadcastReceiver监听电量变化
用BroadcastReceiver监听电量的变化,可以实现BroadcastReceiver接收电量变化的广播,然后获取电量百分比信息. BatteryChangedReceiver.java pu ...
- jquery mobile listview列表属性(搜索自动填充检索效果)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- (转)[jQuery]使用jQuery.Validate进行客户端验证(初级篇)——不使用微软验证控件的理由
以前在做项目的时候就有个很大心病,就是微软的验证控件,虽然微软的验证控件可以帮我们完成大部分的验证,验证也很可靠上手也很容易,但是我就是觉得不爽,主要理由有以下几点: 1.拖控件太麻烦,这个是微软控件 ...
- C#解析JSON几种方式-整理
一.什么是JSON JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于JavaScript(Standard ECMA-262 3rd Edition ...
- [Struts2学习笔记] -- 环境配置
在创建好WebProject后,就可以开始进行Struts2的环境配置,可以到Struts2官网下载,本环境使用struts-2.3.24.1版本. 首先导入必要的jar包到WebProject的/W ...
- 【新手--android日记】实现IOS风格电话界面
[前言--新手日记] 开始学习android开发,通过做一个通讯录练习,打算实现各种自己想实现的功能. 新手作品,技术含量很浅.主要是记录自己的学习过程. 纯学习之用,求评论,求建议,求教导. [正题 ...
- MVC linq语法分页
分页效果图: 表格下面的分页按钮样式是我自己做的一个样式,这4个按钮都是用同一张图片:这张图片是用ps做的. 接下来我们说一下怎么去做这个样式 第一css代码: ._HomePage,._Previo ...