在之前的文章MySQL 常見(jiàn)死鎖場(chǎng)景 -- 并發(fā)Replace into導(dǎo)致死鎖介紹了由于二級(jí)索引 unique key 導(dǎo)致的 deadlock, 其實(shí)主鍵也是 unique 的, 那么同樣其實(shí)主鍵的 unique key check 一樣會(huì)導(dǎo)致死鎖.
主鍵 unique 的判斷在
row_ins_clust_index_entry_low => row_ins_duplicate_error_in_clust
對(duì)于普通的INSERT操作, 當(dāng)需要檢查primary key unique時(shí), 加 S record lock. 而對(duì)于Replace into 或者 INSERT ON DUPLICATE操作, 則加X(jué) record lock
這里check unique 的時(shí)候, 如果這里沒(méi)有這個(gè) record 存在, 加在下一個(gè) record上, 如果已經(jīng)有一個(gè) delete_mark record, 那么就加在這個(gè) delete marked record 上.
例子 1
create table t1 (a int primary key); # 然后有三個(gè)不 session: session1: begin; insert into t1(a) values (2); session2: insert into t1(a) values (2); session3: insert into t1(a) values (2); session1: rollback;
rollback 之前:
這個(gè)時(shí)候 session2/session3 會(huì)wait 在這里2 等待s record lock, 因?yàn)閟ession1 執(zhí)行delete 時(shí)候會(huì)執(zhí)行row_update_for_mysql => lock_clust_rec_modify_check_and_lock
這里會(huì)給要修改的record 加x record lock
insert 的時(shí)候其實(shí)也給record 加 x record lock, 只不過(guò)大部分時(shí)候先加implicit lock, 等真正有沖突的時(shí)候觸發(fā)隱式鎖的轉(zhuǎn)換才會(huì)加上x(chóng) lock
問(wèn)題1: 這里為什么granted lock 里面 record 2 上面有x record lock 和 s record lock?
在session1 執(zhí)行 rollback 以后, session2/session3 獲得了s record lock, 在insert commit 時(shí)候發(fā)現(xiàn)死鎖, rollback 其中一個(gè)事務(wù), 另外一個(gè)提交, 死鎖信息如下
這里看到 trx1 想要 x insert intention lock.
但是trx2 持有s next-key lock 和 trx1 x insert intention lock 沖突.
同時(shí)trx 也在等待 x insert intention lock, 這里從上面的持有Lock 可以看到 肯定在等待trx1 s next-key lock
問(wèn)題: 等待的時(shí)候是 S gap lock, 但是死鎖的時(shí)候發(fā)現(xiàn)是 S next-key lock. 什么時(shí)候進(jìn)行的升級(jí)?
這里問(wèn)題的原因是這個(gè) table 里面只有record 2, 所以這里認(rèn)真看, 死鎖的時(shí)候是等待在 supremum 上的, 因?yàn)閟upremum 的特殊性, supremum 沒(méi)有g(shù)ap lock, 只有 next-key lock
0: len 8; hex 73757072656d756d: asc supremum; // 這個(gè)是等在supremum 記錄
在 2 后面插入一個(gè) 3 以后, 就可以看到在record 3 上面是有s gap lock 并不是next-key lock, 如下圖:
那么這個(gè) gap lock 是哪來(lái)的?
這里gap lock 是在 record 3 上的. 這個(gè)record 3 的s lock 從哪里來(lái)? session2/3 等待在record 2 上的s record lock 又到哪里去了?
這幾涉及到鎖升級(jí), 鎖升級(jí)主要有兩種場(chǎng)景
insert record, 被next-record 那邊繼承鎖. 具體代碼 lock_update_insert
delete record(注意這里不是delete mark, 必須是purge 的物理delete), 需要將該record 上面的lock, 贈(zèng)給next record上, 具體代碼 lock_update_delete
并且由于delete 的時(shí)候, 將該record 刪除, 如果有等待在該record 上面的record lock, 也需要遷移到next-key 上, 比如這個(gè)例子wait 在record 2 上面的 s record lock
另外對(duì)于wait 在被刪除的record 上的trx, 則通過(guò) lock_rec_reset_and_release_wait(block, heap_no); 將這些trx 喚醒
具體看 InnoDB Trx lock
總結(jié):
2 個(gè)trx trx2/trx3 都等待在primary key 上, 鎖被另外一個(gè) trx1 持有. trx1 回滾以后, trx2 和 trx3 同時(shí)持有了該 record 的 s lock, 通過(guò)鎖升級(jí)又升級(jí)成下一個(gè) record 的 GAP lock. 然后兩個(gè) trx 同時(shí)插入的時(shí)候都需要獲得insert_intention lock(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION); 就變成都想持有insert_intention lock, 被卡在對(duì)方持有 GAP S lock 上了.
例子 2
mysql> select * from t1;
+—-+
| a |
+—-+
| 2 |
| 3 |
+—-+
然后有三個(gè)不同 session:
session1: begin; delete from t1 where a = 2; session2: insert into t1(a) values (2); session3: insert into t1(a) values (2); session1: commit;
commit之前
這個(gè)時(shí)候session2/3 都在等待s record 2 lock, 等待時(shí)間是 innodb_lock_wait_timeout,
commit 之后
在session1 執(zhí)行 commit 以后, session2/session3 獲得到正在waiting的 s record lock, 在commit 的時(shí)候, 發(fā)現(xiàn)死鎖, rollback 其中一個(gè)事務(wù), 另外一個(gè)提交, 死鎖信息如下
trx1 等待x record lock, trx2 持有s record lock(這個(gè)是在session1 commit, session2/3 都獲得了s record lock)
不過(guò)這樣發(fā)現(xiàn)和上面例子不一樣的地方, 這里的record 都lock 在record 2 上, 而不是record 3, 這是為什么?
本質(zhì)原因是這里的delete 操作是 delete mark, 并沒(méi)有從 btree 上物理刪除該record, 因此還可以保留事務(wù)的lock 在record 2 上, 如果進(jìn)行了物理刪除操作, 那么這些record lock 都有遷移到next record 了
問(wèn)題: 這里insert 操作為什么不是 insert intention lock?
比如如果是sk insert 操作就是 insert intention lock. 而這里是 s record lock?
這里delete record 2 以后, 由于record 是 delete mark, 記錄還在, 因此insert 的時(shí)候會(huì)將delete mark record改成要寫(xiě)入的這個(gè)record(這里不是可選擇優(yōu)化, 而是btree 唯一性, 必須這么做). 因此插入就變成 row_ins_clust_index_entry_by_modify
所以不是insert 操作, 因此就沒(méi)有 insert intention lock.
而sk insert 的時(shí)候是不允許將delete mark record 復(fù)用的, 因?yàn)閐elete mark record 可能會(huì)被別的readview 讀取到.
通過(guò)GDB + call srv_debug_loop() 可以讓GDB 將進(jìn)程停留在 session1 提交, 但是session2/3 還沒(méi)有進(jìn)入死鎖之前, 這個(gè)時(shí)候查詢performance_schema 可以看到session2/3 獲得了record 10 s lock. 這個(gè)lock 怎么獲得的呢?
這個(gè)和上述的例子一樣, 這里因?yàn)榈鹊谋容^久了, 所以發(fā)生了purge, 因?yàn)閞ecord 2 被物理刪除了. 因此發(fā)生了鎖升級(jí), record 2 上面的record 會(huì)轉(zhuǎn)給next-record, 這里next-record 是10,
總結(jié):
和上一個(gè)例子基本類(lèi)似.
2 個(gè)trx trx2/trx3 都等待在primary key 上的唯一性檢查上, 鎖被另外一個(gè) trx1 持有. trx1 commit 以后, trx2 和 trx3 同時(shí)持有了該 record 的 s record lock, 然后由于 delete mark record 的存在, insert 操作變成 modify 操作, 因此就變成都想持有X record lock, 被卡在對(duì)方持有 S recordlock 上了.
審核編輯:黃飛
-
死鎖
+關(guān)注
關(guān)注
0文章
25瀏覽量
8096 -
MySQL
+關(guān)注
關(guān)注
1文章
831瀏覽量
26762
原文標(biāo)題:MySQL 常見(jiàn)死鎖場(chǎng)景-- 并發(fā)插入相同主鍵場(chǎng)景
文章出處:【微信號(hào):inf_storage,微信公眾號(hào):數(shù)據(jù)庫(kù)和存儲(chǔ)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
MySQL和MongoDB的對(duì)比
藍(lán)牙低功耗常見(jiàn)的應(yīng)用場(chǎng)景及架構(gòu)
死鎖是什么?產(chǎn)生死鎖的主要原因有哪些
DIN中的死鎖避免和死鎖恢復(fù)
![DIN中的<b class='flag-5'>死鎖</b>避免和<b class='flag-5'>死鎖</b>恢復(fù)](https://file1.elecfans.com//web2/M00/A4/98/wKgZomUMNOWAPSMrAAAdiJk8b78874.jpg)
盤(pán)點(diǎn)MySQL常見(jiàn)問(wèn)題及解答
MySQL中的高級(jí)內(nèi)容詳解
![<b class='flag-5'>MySQL</b>中的高級(jí)內(nèi)容詳解](https://file.elecfans.com/web1/M00/E4/79/o4YBAGBJ3WWAQf5ZAAG6DHC8HGU088.png)
MySQL并發(fā)Replace into導(dǎo)致死鎖場(chǎng)景簡(jiǎn)析
![<b class='flag-5'>MySQL</b>并發(fā)Replace into導(dǎo)致<b class='flag-5'>死鎖</b><b class='flag-5'>場(chǎng)景</b>簡(jiǎn)析](https://file1.elecfans.com/web2/M00/89/8C/wKgaomSH232AOX4JAAA3ncrGzmA193.png)
通過(guò)GDB non-stop mode調(diào)試MySQL
![通過(guò)GDB non-stop mode調(diào)試<b class='flag-5'>MySQL</b>](https://file1.elecfans.com/web2/M00/A5/F7/wKgaomUQ8pKAeQwpAAANX3mVYVQ283.png)
Linux內(nèi)核死鎖lockdep功能
![Linux內(nèi)核<b class='flag-5'>死鎖</b>lockdep功能](https://file1.elecfans.com/web2/M00/A6/53/wKgaomUT1P-AO5OKAAAL7WJJNAg682.jpg)
死鎖的產(chǎn)生因素
![<b class='flag-5'>死鎖</b>的產(chǎn)生因素](https://file1.elecfans.com/web2/M00/AD/25/wKgaomVMN5eAX35iAADugPYN18s779.jpg)
死鎖的現(xiàn)象及原理
![<b class='flag-5'>死鎖</b>的現(xiàn)象及原理](https://file1.elecfans.com/web2/M00/AF/4F/wKgZomVN6O2AW1oPAADWK1UkxsE827.jpg)
死鎖的現(xiàn)象以及原理
![<b class='flag-5'>死鎖</b>的現(xiàn)象以及原理](https://file1.elecfans.com/web2/M00/AF/4F/wKgZomVN6O2AW1oPAADWK1UkxsE827.jpg)
評(píng)論