mysql併發insert死鎖問題——gap、插入意向鎖衝突

問題描述

線上出現MySQL死鎖報警,經過show engine innodb status命令查看死鎖日誌,結合異常代碼,還原發生死鎖的事務場景以下:html

環境: mysql5.7,事務隔離級別REPEATABLE-READmysql

表結構sql

CREATE TABLE `ta` (
  `id` int AUTO_INCREMENT,
  `a` int,
  `b` int ,  `c` int ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_a_b` (`a`,`b`)
) ENGINE=InnoDB
數據:
mysql> select * from ta;
+----+------+------+------+
| id | a    | b    | c    |
+----+------+------+------+
|  1 |    1 |   10 |  100 |
|  2 |    3 |   20 |   99 |
|  3 |    5 |   50 |   80 |
+----+------+------+------+

併發事務併發

T1 T2
begin; begin
delete from ta where a = 4;//ok, 0 rows affected
delete from ta where a = 4; //ok, 0 rows affected
insert into ta(a,b,c) values(4, 11, 3),(4, 2, 5);//wating,被阻塞
insert into ta(a,b,c) values(4, 11, 3),(4, 2, 5); //ERROR 1213 (40001): Deadlock found when trying to get lock;
T1執行完成, 2 rows affected

從上面能夠看出,併發事務都成功執行delete後(影響行數爲0),執行insert出現死鎖。分佈式

死鎖分析

等待鎖分析

查看死鎖日誌,顯示事務T1的insert語句在等待插入意向鎖,lock_mode X locks gap before rec insert intention waiting;事務T2持有a=4的gap lock,同時也在等待插入意向鎖。另外,T1能執行delete,說明它也拿到了gap lock,因此,兩個事務都持有gap lock,致使循環等待插入意向鎖而發生死鎖。spa

加鎖分析

  1. delete的where子句沒有知足條件的記錄,而對於不存在的記錄 而且在RR級別下,delete加鎖類型爲gap lock,gap lock之間是兼容的,因此兩個事務都能成功執行delete;關於gap lock能夠參考文章加鎖分析。這裏的gap範圍是索引a列(3,5)的範圍。
  2. insert時,其加鎖過程爲先在插入間隙上獲取插入意向鎖,插入數據後再獲取插入行上的排它鎖。又插入意向鎖與gap lock和 Next-key lock衝突,即一個事務想要獲取插入意向鎖,若是有其餘事務已經加了gap lock或 Next-key lock,則會阻塞。
  3. 場景中兩個事務都持有gap lock,而後又申請插入意向鎖,此時都被阻塞,循環等待形成死鎖。

鎖兼容矩陣.net

鎖兼容矩陣

死鎖解決

方案以下幾種選擇:日誌

  1. 不採用事務包裝這部分邏輯,本文實際業務場景中能夠不須要事務,因此直接取消事務包裝便可,採用insert ON DUPLICATE KEY UPDATE的方式
  2. 調整事務隔離級別爲read commit,RC級別不會產生gap lock
  3. 利用分佈式鎖

附:排查過程

  1. 查看死鎖日誌,注意這裏並不會包含整個事務的相關sql,僅僅會把等待鎖的SQL打印出來,死鎖日誌內容含義參考 :http://blog.itpub.net/22664653/viewspace-2145133/
  2. 根據服務異常log定位到具體事務執行代碼,找出該事務相關的sql
  3. 根據積累的經驗知識分析加鎖、鎖等待狀況,找出死鎖緣由

參考

insert加鎖分析,參考文章http://www.aneasystone.com/archives/2017/12/solving-dead-locks-three.htmlcode

相關文章
相關標籤/搜索