MySQL死鎖案例分析一(先delete,再insert,導(dǎo)致死鎖)

一、死鎖案例

MySQL版本:Percona MySQL Server 5.7.19
隔離級(jí)別:可重復(fù)讀(RR)
業(yè)務(wù)邏輯:并發(fā)下按某個(gè)索引字段先delete記錄,再insert記錄

比如:

begin;
delete from tb where order_id = xxx;
insert into tb(order_id) values(xxx);
commit;

二、MySQL鎖基本概念

S:共享鎖(行級(jí)鎖)
X:排他鎖(行級(jí)鎖)
IS:意向共享鎖(表級(jí)鎖)
IX:意向排他鎖(表級(jí)鎖)

以上4種鎖的兼容性見下表:


鎖模式兼容性表
  • gap鎖與gap鎖之間不沖突
  • rec insert intention(插入意向鎖)與gap鎖沖突。

三、模擬復(fù)現(xiàn)死鎖

打開參數(shù),從innodb status獲取更多的鎖信息。
set GLOBAL innodb_status_output_locks=ON;

表結(jié)構(gòu):

 CREATE TABLE `tb` (
  `order_id` int(11) DEFAULT NULL,
  KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

表中數(shù)據(jù):

mysql> select * from tb;
+----------+
| order_id |
+----------+
|       10 |
|       20 |
+----------+
2 rows in set (0.00 sec)

事務(wù)執(zhí)行步驟:

session1 session2
begin
begin
delete from tb where order_id=15;
delete from tb where order_id=15;
insert into tb select 15;(等待鎖)
insert into tb select 15;(死鎖)
  1. 當(dāng)session1執(zhí)行delete from tb where order_id=15;,由于條件order_id=15的記錄不存在,session1 獲得2個(gè)鎖結(jié)構(gòu),分別是意向排他鎖IX(表級(jí)鎖)、gap鎖(行級(jí)鎖),如下:
---TRANSACTION 1055191443, ACTIVE 20 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 315642, OS thread handle 139960342456064, query id 150462030 localhost root
TABLE LOCK table `db`.`tb` trx id 1055191443 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec
  1. 當(dāng)session2執(zhí)行delete from tb where order_id=15;,同樣由于order_id=15的記錄不存在,session2 也獲得2個(gè)鎖結(jié)構(gòu),分別是意向排他鎖IX(表級(jí)鎖)、gap鎖(行級(jí)鎖),如下:
---TRANSACTION 1055191444, ACTIVE 3 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462412 localhost root
TABLE LOCK table `db`.`tb` trx id 1055191444 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec
  1. 當(dāng)session2執(zhí)行insert into tb select 15;, session2 已經(jīng)獲取到IX鎖,gap鎖,等待 rec insert intention(插入意向鎖)
---TRANSACTION 1055191444, ACTIVE 68 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462778 localhost root executing
insert into tb select 15
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting
------------------
TABLE LOCK table `db`.`tb` trx id 1055191444 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting
  1. 當(dāng)session1執(zhí)行insert into tb select 15;,session1 已獲取到IX鎖,gap鎖, 等待rec insert intention(插入意向鎖), session1, session2 都在等待插入意向鎖, 插入意向鎖與gap鎖沖突,雙方都沒有釋放gap鎖,又都在等待插入意向鎖,死鎖發(fā)生。
LATEST DETECTED DEADLOCK
------------------------
2018-11-03 17:15:11 0x7f4b0e7ea700
*** (1) TRANSACTION:
TRANSACTION 1055191444, ACTIVE 135 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462778 localhost root executing
insert into tb select 15
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1055191443, ACTIVE 201 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315642, OS thread handle 139960342456064, query id 150463172 localhost root executing
insert into tb select 15
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)

四、案例擴(kuò)展

以上死鎖案例,業(yè)務(wù)代碼邏輯是多線程并發(fā)下,有可能多個(gè)線程會(huì)執(zhí)行相同order_id的job,比如兩個(gè)線程執(zhí)行的order_id 都是15。
另外一種情況,多個(gè)線程間,不會(huì)執(zhí)行到相同order_id的情況,也可能發(fā)生死鎖。比如一個(gè)線程order_id=15,另外一個(gè)線程order_id=16,如下所示:

事務(wù)執(zhí)行步驟:

session1 session2
begin
begin
delete from tb where order_id=15;
delete from tb where order_id=16;
insert into tb select 16;(等待鎖)
insert into tb select 15;(死鎖)

鎖情況與上述相同,不再贅述,死鎖信息如下:

LATEST DETECTED DEADLOCK
------------------------
2018-11-03 17:28:30 0x7f4b0e667700
*** (1) TRANSACTION:
TRANSACTION 1055191450, ACTIVE 18 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 316221, OS thread handle 139960338228992, query id 150467652 localhost root executing
insert into tb select 16
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191450 lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1055191449, ACTIVE 28 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 316222, OS thread handle 139960340870912, query id 150467681 localhost root executing
insert into tb select 15
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191449 lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191449 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)

五、解決方案

  1. 修改隔離級(jí)別為提交讀(RC)
  2. 修改業(yè)務(wù)代碼邏輯,刪除記錄之前,先select,確認(rèn)該記錄存在,再執(zhí)行delete刪除該記錄。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 當(dāng)一個(gè)系統(tǒng)訪問量上來的時(shí)候,不只是數(shù)據(jù)庫性能瓶頸問題了,數(shù)據(jù)庫數(shù)據(jù)安全也會(huì)浮現(xiàn),這時(shí)候合理使用數(shù)據(jù)庫鎖機(jī)制就顯得異...
    初來的雨天閱讀 3,695評(píng)論 0 22
  • 一 綜述 MySQL 的鎖機(jī)制相較其他的數(shù)據(jù)庫比較簡單,最顯著的特點(diǎn)是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制。主要有三種類...
    柯基去哪了閱讀 3,347評(píng)論 3 7
  • 今天是10月份的最后一天|【首要任務(wù)】設(shè)置任務(wù)寶的二階任務(wù) 【任務(wù)拆解】修改文案,設(shè)置任務(wù)寶 【思考】多階任務(wù)寶的...
    系統(tǒng)提示我不能叫娜娜閱讀 185評(píng)論 0 1
  • 最近閑著無事想的有點(diǎn)多,回想以前,品味現(xiàn)在,考慮將來,不知道為什么以前的那些事在我腦海中占據(jù)的空間較大。 ...
    店小六A閱讀 194評(píng)論 0 0
  • 標(biāo)頭.h =========================== ========================...
    MagicalGuy閱讀 274評(píng)論 0 0

友情鏈接更多精彩內(nèi)容