问题描述
在做项目的过程中,由于写SQL太过随意,一不小心就抛了一个死锁异常,如下:
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
[java]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
- ????????at?sun.reflect.GeneratedConstructorAccessor247.newInstance(Unknown?Source)??
-
- ????????at?java.lang.reflect.Constructor.newInstance(Constructor.java:
-
- ????????at?com.mysql.jdbc.Util.getInstance(Util.java:
-
- ????????at?com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
-
- ????????at?com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:
-
- ????????at?com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:
-
- ????????at?com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:
-
- ????????at?com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:
-
表结构如下:
<div class="dp-highlighter bg_sql">
<div class="bar">
<div class="tools">
[sql]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
- ??`id`?
-
- ??`item_id`?
-
- ??
-
- )?ENGINE=INNODB?
SQL语句如下:
<div class="dp-highlighter bg_sql">
<div class="bar">
<div class="tools">
[sql]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
原因分析
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、先获取需要更新的记录的主键?
<div class="dp-highlighter bg_sql">
<div class="bar">
<div class="tools">
[sql]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
2、逐条更新
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
[java]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
- ????userItemDAO.updateStatus(id,?userId,?
-
[sql]?
?
这样貌似解决了,都是对单条进行操作,都是先获取主键上的锁,再获取idx_1上的锁。
不过这个解决方案与先前的更新语句不一样,先前的更新语句对所有记录的更新在一个事务中,采用循环更新后并不在同一个事务中,所以在for循环外面还得开一个事务。
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
[java]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">copy<a class="PrintSource" title="print" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">print<a class="About" title="?" href="http://blog.csdn.net/aesop_wubo/article/details/8286215">?
?
- ???
-
- ????????????
- userItemDAO.updateStatus(id,userId,
- ????????}??
-
- ??????}
-
- ?????????
-
- ??????
-
- ?????????
-
- ???}??
-
小结:在采用INNODB的MySQL中,更新操作默认会加行级锁,行级锁是基于索引的,在分析死锁之前需要查询一下mysql的执行计划,看看是否用到了索引,用到了哪个索引,对于没有用索引的操作会采用表级锁。如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁,否则加锁顺序相反。在并发度高的应用中,批量更新一定要带上记录的主键,优先获取主键上的锁,这样可以减少死锁的发生。
原文:http://blog.csdn.net/aesop_wubo/article/details/8286215 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|