【MySQL】死鎖案例之一
死鎖,其實是一個很有意思,也很有挑戰的技術問題,大概每個DBA和部分開發同學都會在工作過程中遇見過 。關於死鎖我會持續寫一個系列的案例分析,希望能夠對想了解死鎖的朋友有所幫助。
二 案例分析
2.1 環境說明
MySQL 5.6 事務隔離級別為RR
-
CREATE TABLE `ty` (
-
`id` int(11) NOT NULL AUTO_INCREMENT,
-
`a` int(11) DEFAULT NULL,
-
`b` int(11) DEFAULT NULL,
-
PRIMARY KEY (`id`),
-
KEY `idxa` (`a`)
-
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
- insert into ty(a,b) values(2,3),(5,4),(6,7);
T2 |
T1 |
begin; |
|
delete from ty where a=5; |
begin; |
|
delete from ty where a=5; |
insert into ty(a,b) values(2,10); |
|
|
delete from ty where a=5; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
2.3 死鎖日誌
-
------------------------
-
LATEST DETECTED DEADLOCK
-
------------------------
-
2017-09-09 22:34:13 7f78eab82700
-
*** (1) TRANSACTION:
-
TRANSACTION 462308399, ACTIVE 33 sec starting index read
-
mysql tables in use 1, locked 1
-
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
-
MySQL thread id 3525577, OS thread handle 0x7f896cc4b700, query id 780039657 localhost root updating
-
delete from ty where a=5
-
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
-
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308399 lock_mode X waiting
-
*** (2) TRANSACTION:
-
TRANSACTION 462308398, ACTIVE 61 sec inserting, thread declared inside InnoDB 5000
-
mysql tables in use 1, locked 1
-
5 lock struct(s), heap size 1184, 4 row lock(s), undo log entries 2
-
MySQL thread id 3525490, OS thread handle 0x7f78eab82700, query id 780039714 localhost root update
-
insert into ty(a,b) values(2,10)
-
*** (2) HOLDS THE LOCK(S):
-
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X
-
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
-
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X locks gap before rec insert intention waiting
- *** WE ROLL BACK TRANSACTION (1)
首先要理解的是 對同一個欄位申請加鎖是需要排隊. S GAP 於
其次表ty中a為普通索引欄位,我們根據事務執行的時間順序來解釋,這樣比較好理解。
a 根據死鎖日誌顯示 事務2 也即sess1執行的事務,根據 HOLDS THE LOCK(S)顯示
sess1 先執行 delete from ty where a=5 ,該事務持有索引a=5 的行鎖lock_mode X ,因為是RR隔離級別,所以sess1 還持有兩個gap鎖[1,2]-[2,5], [2,5]-[3,6] 。
b 事務1的日誌也即sess2執行的事務,申請對 a=5 加鎖,一個rec lock 和兩個gap鎖,因為sess1中delete還沒釋放,故sess2的事務1等待sess1的事務2釋放a=5的鎖資源。
c 然後根據WAITING FOR THIS LOCK TO BE GRANTED,提示事務2 insert語句正在等待 lock_mode X locks gap before rec insert intention waiting,
因為insert語句 [4,2] 介於gap鎖[1,2]-[2,5]之間,所以有了提示 "lock_mode X locks gap",insert語句必須等待前面 sess2中delete 獲取鎖並且釋放鎖。於是,sess2(delete) 等待sess1(delete) ,sess1(insert)等待sess2(delete),迴圈等待,造成死鎖。
問題 如果sess1 執行 insert into ty(a,b) values(5,10); sess2會遇到死鎖嗎?
三 案例二
3.1 索引為唯一鍵
MySQL 5.6 事務隔離級別為RR
-
CREATE TABLE `t2` (
-
`id` int(11) NOT NULL AUTO_INCREMENT,
-
`a` int(11) DEFAULT NULL,
-
`b` int(11) DEFAULT NULL,
-
PRIMARY KEY (`id`),
-
unique KEY `idxa` (`a`)
-
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
- insert into t2(a,b) values(2,3),(5,4),(6,7)
T2 |
T1 |
begin; |
|
delete from ty where a=5; |
begin; |
|
delete from ty where a=5; |
insert into ty(a,b) values(2,10); |
|
|
delete from ty where a=5; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
3.3 死鎖日誌
-
------------------------
-
LATEST DETECTED DEADLOCK
-
------------------------
-
2017-09-10 00:03:31 7f78ea936700
-
*** (1) TRANSACTION:
-
TRANSACTION 462308445, ACTIVE 9 sec starting index read
-
mysql tables in use 1, locked 1
-
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
-
MySQL thread id 3526009, OS thread handle 0x7f896cc4b700, query id 780047877 localhost root updating
-
delete from t2 where a=5
-
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
-
RECORD LOCKS space id 221 page no 4 n bits 72 index `idxa` of table `test`.`t2` trx id 462308445 lock_mode X waiting
-
*** (2) TRANSACTION:
-
TRANSACTION 462308444, ACTIVE 17 sec inserting, thread declared inside InnoDB 5000
-
mysql tables in use 1, locked 1
-
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 2
-
MySQL thread id 3526051, OS thread handle 0x7f78ea936700, query id 780047890 localhost root update
-
insert t2(a,b) values(5,10)
-
*** (2) HOLDS THE LOCK(S):
-
RECORD LOCKS space id 221 page no 4 n bits 72 index `idxa` of table `test`.`t2` trx id 462308444 lock_mode X locks rec but not gap
-
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
-
RECORD LOCKS space id 221 page no 4 n bits 72 index `idxa` of table `test`.`t2` trx id 462308444 lock mode S waiting
- *** WE ROLL BACK TRANSACTION (1)
首先我們要特別說明delete的加鎖邏輯
-
a 找到滿足條件的記錄,並且記錄有效,則對記錄加X鎖,No Gap鎖(lock_mode X locks rec but not gap);
-
b 找到滿足條件的記錄,但是記錄無效(標識為刪除的記錄),則對記錄加next key鎖(同時鎖住記錄本身,以及記錄之前的Gap:lock_mode X);
- c 未找到滿足條件的記錄,則對第一個不滿足條件的記錄加Gap鎖,保證沒有滿足條件的記錄插入(locks gap before rec)
本例我們依然根據事務執行的時間順序來解釋,這樣比較好理解。
a 根據死鎖日誌顯示 事務2 也即sess1執行的事務,根據 HOLDS THE LOCK(S)顯示
sess1 先執行 delete from ty where a=5 ,該事務持有索引a=5 的行鎖lock_mode X locks rec but not gap。因為本例中a是唯一鍵,故沒有gap鎖。
b 事務1的日誌也即sess2執行的事務,申請對 a=5 加鎖(X Next-key Lock),一個rec lock 但是因為sess1中delete 已經執行完成,記錄無效沒有被刪除,鎖還沒釋放,故sess2的事務1等待sess1的事務2釋放a=5的鎖資源,日誌中提示 lock_mode X waiting.
c 然後根據WAITING FOR THIS LOCK TO BE GRANTED,提示事務2 insert語句正在等待 lock mode S waiting,為什麼這次是 S 鎖呢?因為a欄位是一個唯一索引,所以insert語句會在插入前進行一次duplicate key的檢查,需要申請S鎖防止其他事務對a欄位進行重複插入。而插入意向鎖與T1已經insert語句必須等待前面 sess2中delete 獲取a=5的行鎖並且釋放鎖。
於是,sess2(delete) 等待sess1(delete) ,sess1(insert)等待sess2(delete),迴圈等待,造成死鎖。
四 小結
本文研究了RR事務隔離級別下,普通索引與唯一鍵兩種情況的死鎖場景。如何避免解決此類死鎖?推薦使用RC隔離級別+ ROW BASE BINLOG . 但是對於RC/RR模式下 ,insert 遇到唯一鍵衝突的時候的死鎖不可避免。需要開發在設計表結構的時候 減少unique 索引設計。推薦文章 《不同語句模式下的鎖型別》
如果您覺得能從本文收益,可以請北在南方一瓶飲料 ^_^
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-2145073/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【MySQL】死鎖案例之六MySql
- 【MySQL】死鎖案例之七MySql
- 【MySQL】死鎖案例之八MySql
- 【MySQL】死鎖案例之四MySql
- 【MySQL】死鎖案例之二MySql
- 【MySQL】死鎖案例之三MySql
- MySQL批量更新死鎖案例分析MySql
- MySQL:Innodb 一個死鎖案例MySql
- 故障分析 | MySQL死鎖案例分析MySql
- MySQL死鎖案例一(回滾導致死鎖)MySql
- MySQL死鎖案例二(自增列導致死鎖)MySql
- 剖析6個MySQL死鎖案例的原因以及死鎖預防策略MySql
- 死鎖案例分析
- MySQL死鎖案例 – Learn. Write. Repeat.MySql
- GreatSQL 死鎖案例分析SQL
- MySQL死鎖案例分析一(先delete,再insert,導致死鎖)MySqldelete
- MySQL 死鎖和鎖等待MySql
- 面試:什麼是死鎖,如何避免或解決死鎖;MySQL中的死鎖現象,MySQL死鎖如何解決面試MySql
- MySQL死鎖系列-線上死鎖問題排查思路MySql
- MySQL 死鎖解決MySql
- MySQL死鎖問題MySql
- 【MySQL】漫談死鎖MySql
- MySQL解決死鎖MySql
- mysql行鎖和死鎖檢測MySql
- Mysql 兩階段鎖和死鎖MySql
- oracle deadlock死鎖trace file分析之一Oracle
- MySQL:一個死鎖分析 (未分析出來的死鎖)MySql
- MySQL 死鎖問題分析MySql
- MySQL:死鎖一例MySql
- Mysql如何處理死鎖MySql
- MySQL:MTS和mysqldump死鎖MySql
- MySQL列印死鎖日誌MySql
- MySQL 死鎖日誌分析MySql
- mysql死鎖最佳化MySql
- MySQL鎖等待與死鎖問題分析MySql
- MySQL InnoDB如何應付死鎖MySql
- 【MySQL】InnoDB鎖機制之一MySql
- oracle deadlock死鎖trace file分析之一增補Oracle