Mysql 死鎖場(chǎng)景一(insert on duplicate key)

數(shù)據(jù)準(zhǔn)備:

create table t(c1 int primary key, c2 int, c3 int, c4 int, unique index i_c2(c2), index i_c3(c3));

insert into t values (10, 11, 12, 13), (20, 21, 22, 23), (30, 31, 32, 33), (40, 41, 42, 43);

表名t,c1列為主鍵,c2列為唯一索引,c3列為普通索引
數(shù)據(jù)庫隔離級(jí)別:RR
數(shù)據(jù)庫版本:mysql 5.7.21

鎖阻塞示意圖(后續(xù)分析的時(shí)候,用得到):


image.png

死鎖場(chǎng)景

場(chǎng)景說明:批量插入場(chǎng)景,多個(gè)會(huì)話同時(shí)插入,每一個(gè)會(huì)話插入多條數(shù)據(jù)。
場(chǎng)景描述:在唯一索引c2的間隙(31,41)插入3條不同記錄。會(huì)話1先插入1條c2=36,會(huì)話2接著插入1條c2=35,最后會(huì)話1插入1條c2=34。這3次操作插入的值都不一樣
會(huì)話1:

start transaction;
insert into t values(50,36,52,53) on duplicate key update c2=36;

會(huì)話2:

start transaction;
insert into t values(60,35,62,63) on duplicate key update c2=35;

這個(gè)時(shí)候會(huì)話2阻塞了,我們可以查看一下鎖信息
另外開啟一個(gè)會(huì)話3

show engine innodb status;


image.png

暫不分析這個(gè)鎖信息,后面一起分析

接著,會(huì)話1:

insert into t values(70,34,72,73) on duplicate key update c2=34;

這個(gè)時(shí)候某一個(gè)會(huì)話會(huì)顯示如下信息:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

我們切換到會(huì)話3

show engine innodb status;


image.png

可以看到最新的1條死鎖信息,就是我們剛剛產(chǎn)生的死鎖

分析:

說在前面:第一張圖命名為圖1,第二張圖命名為圖2

一. 查看圖1,我們知道insert會(huì)上3把鎖

1.1 表鎖IX,這個(gè)沒有任何問題,插入期間,表結(jié)構(gòu)不能變
1.2 間隙鎖(lock_model X locks gap before rec),這個(gè)比較難理解。我看了半天原因也沒看明白想。(如果insert語句命中了記錄,比如已經(jīng)存在c2=36的記錄,這里會(huì)是next-key lock)如果你想細(xì)細(xì)研究,參考如下文章https://juejin.im/entry/5adca48df265da0b9c1037c8
總之,因?yàn)閙ysql5.6有一個(gè)bug,mysql5.7為了修復(fù)這個(gè)bug,引入了這把鎖
1.3 插入意向鎖(lock_mode X locks gap before rec insert intention waiting),插入的時(shí)候,一般都有這把鎖。目的是,告訴別人我要在某某區(qū)間插入數(shù)據(jù)了,避免幻讀(RC隔離級(jí)別不會(huì)加,這里我80%的把握,如果RC隔離級(jí)別會(huì)加,還望大神指正)。對(duì)意向鎖感興趣,可以參考如下文章:http://yeshaoting.cn/article/database/mysql%20insert%E9%94%81%E6%9C%BA%E5%88%B6/
1.4 還有最后一個(gè)紅框,是lock_mode X locks rec but not gap 這個(gè)是插入記錄之后的記錄鎖。
這里捋一下:
會(huì)話1插入記錄的時(shí)候,申請(qǐng)表鎖IX,gap鎖,插入意向鎖。插入成功后,釋放了插入意向鎖,增加了插入記錄的記錄鎖(rec lock)(理論上鎖在事務(wù)未提交,不應(yīng)該釋放,但是看截圖是釋放了的,估計(jì)記錄鎖也能夠保證插入意向鎖的功能)
會(huì)話2插入記錄的時(shí)候,申請(qǐng)表鎖IX,gap鎖。然后申請(qǐng)意向鎖發(fā)生了等待,因?yàn)闀?huì)話1持有了gap鎖。

二. 查看圖2,我們可以看到死鎖原因

會(huì)話2總共3把鎖,等待插入意向鎖,另外持有的2把鎖沒有顯示,經(jīng)過圖1的分析,可以猜到是表鎖IX和間隙鎖gap lock。
會(huì)話1持有3把鎖,等待1把鎖。持有表鎖IX,gap鎖,第一條插入記錄的record lock鎖(第一條記錄申請(qǐng)的插入意向鎖已經(jīng)釋放)
會(huì)話1插入第三條記錄的時(shí)候,先申請(qǐng)gap鎖,發(fā)現(xiàn)已經(jīng)持有,成功(我猜的)。申請(qǐng)插入意向鎖發(fā)生了等待,因?yàn)闀?huì)話2持有g(shù)ap鎖,阻塞了插入意向鎖。
總之,死鎖產(chǎn)生原因是,會(huì)話2的插入意向鎖等待會(huì)話1釋放gap鎖,會(huì)話1插入意向鎖也在等待會(huì)話2釋放gap鎖。

三. 如何規(guī)避

  1. 隔離級(jí)別調(diào)整為RC,讀提交,在這種場(chǎng)景不會(huì)有g(shù)ap鎖(并不是說RC隔離級(jí)別不會(huì)有g(shù)ap鎖)。前提是binlog同步機(jī)制是基于row,而不是基于statement。這一塊不熟悉的,可以參考
    http://www.itdecent.cn/p/c16686b35807
    這里多說一句,建議大家都把隔離級(jí)別調(diào)整為RC,然后binlog基于row。這樣能夠減少很多死鎖的發(fā)生(因?yàn)樗梨i一般是因?yàn)間ap鎖,而RR隔離級(jí)別很多場(chǎng)景都會(huì)有g(shù)ap鎖。而RC隔離級(jí)別只有很少場(chǎng)景存在gap鎖)
  2. 改寫為先select 如果存在update,不存在insert
最后編輯于
?著作權(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)容

  • 1. mysql鎖知多少 我們進(jìn)行insert,update,delete,select會(huì)加鎖嗎,如果加鎖,加鎖步...
    liwsh閱讀 5,208評(píng)論 0 4
  • 前言 數(shù)據(jù)庫鎖定機(jī)制是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性而使各種共享資源在并發(fā)訪問時(shí)變的有序的一種規(guī)則。MySQL數(shù)據(jù)庫的...
    Justlearn閱讀 1,814評(píng)論 0 4
  • 最近有網(wǎng)友遇到了在RR隔離級(jí)別下insert A select B where B.COL=** 由于select...
    重慶八怪閱讀 2,368評(píng)論 6 2
  • 當(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
  • 今天是個(gè)什么日子,鬧鐘失聲,幸福的多睡了一個(gè)小時(shí),喜痛相伴的結(jié)果就是遲到已經(jīng)成為了自然。 今天是個(gè)什么日子,抱天培...
    純流氓藝術(shù)家閱讀 289評(píng)論 0 0

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