【MYSQL死鎖問題】Deadlock found when trying to get lock;

問題:php

在多訪問的狀況下,一個刪除計劃的操做會出現死鎖現象,報錯以下:java

### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may involve com.longfor.tender.mapper.TdPlaInfoMapper.updatePlanDelById-Inline
### The error occurred while setting parameters
### SQL: UPDATE td_plan SET is_delete = ?,   last_update_time=?,   last_update_by = ?   WHERE id = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
; SQL []; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:263)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:71)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:364)
	at com.sun.proxy.$Proxy20.update(Unknown Source)
	at org.mybatis.spring.Sq

解決方案:mysql

此刪除涉及到事物,在刪除計劃的過程當中,還要刪除立項信息、合同需求信息關聯的計劃信息。在刪除開始以前設置事物,是不夠嚴謹的。因此,把開啓事物放置到刪除立項的方法、合同需求方法、計劃的方法,這樣,每一模塊是一個單獨的事物。鎖的範圍縮小,基本併發可用。spring

網上搜索到的資料,以備參考:sql

表結構:
CREATE TABLE `test` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘主鍵’,
`test_id` bigint(20) DEFAULT NULL,
`group_id` bigint(20) DEFAULT NULL COMMENT ‘Id,對應test_group.id’,
`gmt_created` datetime DEFAULT NULL COMMENT ‘建立時間’,
`gmt_modified` datetime DEFAULT NULL COMMENT ‘修改時間’,
`is_deleted` tinyint(4) DEFAULT ‘0’ COMMENT ‘刪除。’,
PRIMARY KEY (`id`),
KEY `idx_testid` (`test_id`),
KEY `idx_groupid` (`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7429111 ;mybatis

SQL執行計劃:
mysql>explain UPDATE test SET is_deleted = 1 WHERE group_id = 1332577 and test_id = 4212859併發

因此第一個事務先根據group_id索引,已經鎖住primary id,而後再根據test_id索引,鎖定primary id;第二個事務先根據test_id索引,已經鎖住primary id,而後再根據group_id索引,去鎖primary id;因此這樣併發更新就可能出現死索引。app

MySQL官方也已經確認了此bug:https://bugs.mysql.com/bug.php?id=77209less

解決方法有兩種:優化

第1、添加test_id+group_id的組合索引,這樣就能夠避免掉index merge;

第2、將優化器的index merge優化關閉;

建議選擇第一種方法來避免此問題的發生。

update時,若是where條件裏面涉及多個字段,區分度都比較高且字段都分別建了索引的話,mysql會多個索引各走一遍,而後結果取個交集;
單條記錄更新不會引起問題; 多條記錄併發更新時,若是索引行數有重疊,因加鎖順序可能不一樣,互相等待可能會致使死鎖,爲何加鎖順序會不一樣呢?咱們的sql中where條件的順序是必定的,那麼加鎖順序也應該必定,爲何會有加鎖順序不一樣狀況。狀況是這樣的,由於咱們使用的是兩個單值索引,where條件中是複合條件,那麼mysql會使用index merge進行優化,優化過程是mysql會先用索引1進行掃表,在用索引2進行掃表,而後求交集造成一個合併索引。這個使用索引掃表的過程和咱們自己的sql使用索引的順序可能存在互斥,因此形成了死鎖。

更多問題說明及解決方案請參見https://bugs.mysql.com/bug.php?id=77209

另一個mysql死鎖的場景

在事務中用for循環更新一張表,這張表中有主鍵和二級索引,更新就是以二級索引爲條件,這時候,由於for循環裏面執行的循序不必定,因此有可能致使死鎖

原文:https://blog.csdn.net/zheng0518/article/details/54695605 ;

mysql 官網參考

[1 Jun 2015 11:31] Andrii Nikitin

Description:
On some conditions UPDATE query uses index merge when both indexes expect to retrieve 1 row.

This behavior increases chances for deadlock.

(Corresponding SELECT doesn't show index merge)

How to repeat:
drop table if exists a;

CREATE TABLE `a` (
  `ID` int  AUTO_INCREMENT PRIMARY KEY,
  `NAME` varchar(21),
  `STATUS` int,
  KEY `NAME` (`NAME`),
  KEY `STATUS` (`STATUS`)
) engine = innodb;

set @N=0;
insert into a(ID,NAME,STATUS)
select
	@N:=@N+1,
	@N%1600000, 
	floor(rand()*4)
 from information_schema.global_variables a, information_schema.global_variables b, information_schema.global_variables c 
LIMIT 1600000;

update a set status=5 where rand() < 0.005 limit 1;

explain UPDATE a SET STATUS = 2 WHERE NAME =  '1000000' AND STATUS = 5;

+-------------------------+--------------+------------------------------------------------------
| type        key         | key_len rows | Extra                                                
+-------------------------+--------------+------------------------------------------------------
| index_merge NAME,STATUS | 24,5       1 | Using intersect(NAME,STATUS); Using where; Using temp
+-------------------------+--------------+------------------------------------------------------

Suggested fix:
Do not use index merge when single index is good enough
Try to avoid using index merge in UPDATE to not provoke deadlocks

[25 Feb 2016 18:38] Paul Dubois

Noted in 5.6.30, 5.7.12, 5.8.0 changelogs.

For some queries, an Index Merge access plan was choosen over a range
scan when the cost for the range scan was the same or less.
相關文章
相關標籤/搜索