記一次Index-Merge造成的死鎖

起因

前一段時(shí)間一個(gè)沒(méi)有多少量的項(xiàng)目突然線上出錯(cuò)報(bào)警哗咆,第一時(shí)間查到異常日志

報(bào)錯(cuò)信息比較明顯,數(shù)據(jù)庫(kù)產(chǎn)生死鎖尺锚。

分析

分析代碼之前讓我們來(lái)復(fù)習(xí)一下什么是死鎖以及產(chǎn)生死鎖的原因是什么

死鎖產(chǎn)生原因是什么?

當(dāng)兩個(gè)及以上的事務(wù)吼蚁,都在等待對(duì)方釋放已經(jīng)持有的鎖或因?yàn)榧渔i順序不一致造成循環(huán)等待鎖資源。

舉個(gè)我們最常見(jiàn)的例子辙谜,A 事務(wù)持有X 俺榆,申請(qǐng)Y,B 事務(wù)持有Y鎖装哆,申請(qǐng)X鎖罐脊。A和B 事務(wù)持有鎖并且申請(qǐng)對(duì)方持有的鎖定嗓,這樣就會(huì)造成死鎖。

翻譯成代碼:

// 在隔離級(jí)別RR萍桌,ID為主鍵索引的情況下宵溅。 (畫(huà)外音:不談隔離級(jí)別與索引情況下分析加鎖都是耍流氓) ? ? ? ? ? ? ? ? ? ? ? ?????????

session1:update name = “a” where id =1;update name = “b” where id =4上炎;?session2: ? update name = “a” where id =?4恃逻;update name = “b” where id =?1;

在并發(fā)的情況下藕施,假設(shè)請(qǐng)求順序是這樣的1. session1先拿id=1的行鎖2. session2拿id=4的行鎖3. session1請(qǐng)求id=4的行鎖(等待session2釋放)4. session2請(qǐng)求id=1的行鎖(等待session1釋放)5. 循環(huán)等待寇损,造成死鎖

了解了死鎖產(chǎn)生的基本原因之后,讓我們?nèi)タ聪略创a裳食,看是不是有類似這樣的代碼邏輯矛市。

但是奇怪的是,我們翻了源碼(這里不把源碼放出來(lái)了)诲祸,但是源碼并沒(méi)有類似這樣的邏輯浊吏,更神奇的是代碼里根本就沒(méi)有@Transaction注解,也就意味著沒(méi)有應(yīng)用到事務(wù)救氯,也就是說(shuō)單表語(yǔ)句造成了死鎖?

現(xiàn)在看來(lái)問(wèn)題比較詭異卿捎,單條語(yǔ)句造成了死鎖。接下來(lái)我們?nèi)ジ鶧BA要一下死鎖日志

根據(jù)死鎖日志径密,發(fā)現(xiàn)確實(shí)僅僅是因?yàn)?

update g_growth_free_activity_product? ? SET buy_count = 1079? ? where product_id = 79550 and activity_id = 2062 and deleted = 0

這條語(yǔ)句產(chǎn)生了死鎖午阵。

轉(zhuǎn)機(jī)

之后就是各種google,百度的時(shí)候了享扔,終于我們發(fā)現(xiàn)了一些和我們比較像的案例底桂,https://blog.csdn.net/zheng0518/article/details/54695605?,鏈接里的例子和我們的現(xiàn)象比較接近惧眠,文章里更是貼出了MySQL官方bug的地址https://bugs.mysql.com/bug.php?id=77209籽懦。

上面的圖就bug中描述的內(nèi)容,大意是update時(shí)使用index merge增加了死鎖風(fēng)險(xiǎn)氛魁。

我們需要先去看看【index-merge】是什么暮顺。
我們翻一下官方文檔:https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html,文檔里有多種情況的介紹秀存,不贅述捶码。
翻譯一下大概就是對(duì)單個(gè)表的多個(gè)索引分別進(jìn)行掃描并將結(jié)果交并集處理。

那我們?cè)賮?lái)看業(yè)務(wù)SQL或链,針對(duì)
update g_growth_free_activity_product? ? SET buy_count = 1079? ? where product_id = 79550 and activity_id = 2062 and deleted = 0
如果有index merge惫恼,意味著【product_id】和【activity_id】是索引列。(deleted字段應(yīng)該沒(méi)人加索引吧)
我們?nèi)タ聪卤碇械乃饕Y(jié)構(gòu):

【product_id】和【activity_id】確實(shí)都是普通二級(jí)索引澳盐。
雖然都是單列索引祈纯,但是我們還不能確定優(yōu)化器在執(zhí)行SQL的時(shí)候一定會(huì)選擇【index merge】令宿,還需要查看下執(zhí)行計(jì)劃。
為了保證我們的數(shù)據(jù)和線上一致腕窥,我們把線上數(shù)據(jù)拉了下來(lái)粒没,并創(chuàng)建了一個(gè)test表,表結(jié)構(gòu)相同簇爆,索引結(jié)構(gòu)相同革娄,把數(shù)據(jù)導(dǎo)進(jìn)去。并查詢到發(fā)生死鎖時(shí)的請(qǐng)求日志

我們通過(guò)執(zhí)行計(jì)劃能看到冕碟,update時(shí)確實(shí)使用了 【index-merge】進(jìn)行優(yōu)化拦惋,extra列顯示的是使用了【交集】類型。

到這時(shí)候安寺,所有的條件就都能對(duì)的上了厕妖,但是是否真的是因?yàn)檫@個(gè)原因發(fā)生死鎖我們還需要還原一下案發(fā)現(xiàn)場(chǎng),嘗試在neibu環(huán)境進(jìn)行復(fù)現(xiàn)挑庶。
我們?cè)谏厦嬉呀?jīng)建好的test表上做測(cè)試言秸。

10個(gè)線程并發(fā)執(zhí)行更新,查看結(jié)果迎捺。

確實(shí)是很容易就發(fā)生了死鎖举畸。
到這里我們問(wèn)題就已經(jīng)基本定位了:由于索引設(shè)置不合理的緣故,where條件兩個(gè)單列普通二級(jí)索引在查詢的時(shí)候MySQL進(jìn)行了index-merge優(yōu)化凳枝,引發(fā)死鎖抄沮。

分析

找到原因之后,我們?cè)賮?lái)分下下使用index-merge為什么會(huì)發(fā)生死鎖岖瑰。

我們先拿到死鎖的數(shù)據(jù)叛买。
查詢【activity_id=2062】,對(duì)應(yīng)MySQL主鍵記錄是在 【88-234】
查詢【product_id=79550】蹋订,對(duì)應(yīng)MySQL主鍵記是【218率挣,186】
查詢【product_id=79466】,對(duì)應(yīng)MySQL主鍵記是【219露戒,183】我們根據(jù)執(zhí)行計(jì)劃與加鎖流程拆分下成如下幾個(gè)過(guò)程

再結(jié)合的死鎖日志椒功,我們分析下加鎖流程

session1等待【activity_id】的鎖,session2等待的是主鍵鎖智什,產(chǎn)生循環(huán)等待动漾,發(fā)生死鎖。

解決方案

最后說(shuō)一下解決方案撩鹿,建議使用方案2谦炬,本身就是因?yàn)樗饕褂貌缓侠韺?dǎo)致,優(yōu)化之后再也沒(méi)有死鎖的問(wèn)題了节沦。

1键思、關(guān)閉【index merge】
2、建立聯(lián)合索引
3甫贯、優(yōu)化代碼
4吼鳞、強(qiáng)制走單列索引

最后提幾個(gè)關(guān)于MySQL建議閱讀的文檔:
官方文檔:https://dev.mysql.com/doc/
淘寶數(shù)據(jù)庫(kù)內(nèi)核月報(bào):http://mysql.taobao.org/monthly/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叫搁,隨后出現(xiàn)的幾起案子赔桌,更是在濱河造成了極大的恐慌,老刑警劉巖渴逻,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾党,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惨奕,警方通過(guò)查閱死者的電腦和手機(jī)雪位,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)梨撞,“玉大人雹洗,你說(shuō)我怎么就攤上這事∥圆ǎ” “怎么了时肿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)港粱。 經(jīng)常有香客問(wèn)我螃成,道長(zhǎng),這世上最難降的妖魔是什么查坪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任锈颗,我火速辦了婚禮,結(jié)果婚禮上咪惠,老公的妹妹穿的比我還像新娘击吱。我一直安慰自己,他們只是感情好遥昧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布覆醇。 她就那樣靜靜地躺著,像睡著了一般炭臭。 火紅的嫁衣襯著肌膚如雪永脓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天鞋仍,我揣著相機(jī)與錄音常摧,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛落午,可吹牛的內(nèi)容都是我干的谎懦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼溃斋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼界拦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起梗劫,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤享甸,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梳侨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蛉威,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年走哺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚯嫌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡割坠,死狀恐怖齐帚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彼哼,我是刑警寧澤对妄,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站敢朱,受9級(jí)特大地震影響剪菱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拴签,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一孝常、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚓哩,春花似錦构灸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至曹阔,卻和暖如春半开,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赃份。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工寂拆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奢米,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓纠永,卻偏偏與公主長(zhǎng)得像鬓长,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渺蒿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345