0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

淺談MySQL常見死鎖場景

數(shù)據(jù)庫和存儲(chǔ) ? 來源:數(shù)據(jù)庫和存儲(chǔ) ? 2024-03-21 14:10 ? 次閱讀

在之前的文章MySQL 常見死鎖場景 -- 并發(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 record lock

這里check unique 的時(shí)候, 如果這里沒有這個(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, 只不過大部分時(shí)候先加implicit lock, 等真正有沖突的時(shí)候觸發(fā)隱式鎖的轉(zhuǎn)換才會(huì)加上x lock

8d27b792-e72e-11ee-a297-92fbcf53809c.png

問題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è)提交, 死鎖信息如下

8d3dfa66-e72e-11ee-a297-92fbcf53809c.png

這里看到 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

問題: 等待的時(shí)候是 S gap lock, 但是死鎖的時(shí)候發(fā)現(xiàn)是 S next-key lock. 什么時(shí)候進(jìn)行的升級(jí)?

這里問題的原因是這個(gè) table 里面只有record 2, 所以這里認(rèn)真看, 死鎖的時(shí)候是等待在 supremum 上的, 因?yàn)閟upremum 的特殊性, supremum 沒有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, 如下圖:

8d43417e-e72e-11ee-a297-92fbcf53809c.png

那么這個(gè) gap lock 是哪來的?

這里gap lock 是在 record 3 上的. 這個(gè)record 3 的s lock 從哪里來? session2/3 等待在record 2 上的s record lock 又到哪里去了?

這幾涉及到鎖升級(jí), 鎖升級(jí)主要有兩種場景

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, 則通過 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, 通過鎖升級(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之前

8d57c40a-e72e-11ee-a297-92fbcf53809c.png

這個(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è)提交, 死鎖信息如下

8d5c4426-e72e-11ee-a297-92fbcf53809c.png

trx1 等待x record lock, trx2 持有s record lock(這個(gè)是在session1 commit, session2/3 都獲得了s record lock)

不過這樣發(fā)現(xiàn)和上面例子不一樣的地方, 這里的record 都lock 在record 2 上, 而不是record 3, 這是為什么?

本質(zhì)原因是這里的delete 操作是 delete mark, 并沒有從 btree 上物理刪除該record, 因此還可以保留事務(wù)的lock 在record 2 上, 如果進(jìn)行了物理刪除操作, 那么這些record lock 都有遷移到next record 了

問題: 這里insert 操作為什么不是 insert intention lock?

比如如果是sk insert 操作就是 insert intention lock. 而這里是 s record lock?

8d76dea8-e72e-11ee-a297-92fbcf53809c.png

這里delete record 2 以后, 由于record 是 delete mark, 記錄還在, 因此insert 的時(shí)候會(huì)將delete mark record改成要寫入的這個(gè)record(這里不是可選擇優(yōu)化, 而是btree 唯一性, 必須這么做). 因此插入就變成 row_ins_clust_index_entry_by_modify

所以不是insert 操作, 因此就沒有 insert intention lock.

而sk insert 的時(shí)候是不允許將delete mark record 復(fù)用的, 因?yàn)閐elete mark record 可能會(huì)被別的readview 讀取到.

8d95d970-e72e-11ee-a297-92fbcf53809c.png

通過GDB + call srv_debug_loop() 可以讓GDB 將進(jìn)程停留在 session1 提交, 但是session2/3 還沒有進(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è)例子基本類似.

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 上了.

審核編輯:黃飛

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 死鎖
    +關(guān)注

    關(guān)注

    0

    文章

    25

    瀏覽量

    8059
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    791

    瀏覽量

    26351

原文標(biāo)題:MySQL 常見死鎖場景-- 并發(fā)插入相同主鍵場景

文章出處:【微信號(hào):inf_storage,微信公眾號(hào):數(shù)據(jù)庫和存儲(chǔ)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    淺談原理圖和PCB圖的常見錯(cuò)誤

    淺談原理圖和PCB圖的常見錯(cuò)誤
    發(fā)表于 08-12 13:04

    MySQL和MongoDB的對(duì)比

    中的文檔不需要具有相同的一組字段,數(shù)據(jù)的非規(guī)范化是常見的。 MongoDB還設(shè)計(jì)了高可用性和可擴(kuò)展性,并提供了即用型復(fù)制和自動(dòng)分片功能。術(shù)語和概念 MySQL中的許多概念在MongoDB中具有相近
    發(fā)表于 08-28 14:51

    藍(lán)牙低功耗常見的應(yīng)用場景及架構(gòu)

    淺談藍(lán)牙低功耗(BLE)的幾種常見的應(yīng)用場景及架構(gòu)
    發(fā)表于 06-15 09:51

    DIN中的死鎖避免和死鎖恢復(fù)

    DIN中的死鎖避免和死鎖恢復(fù) 由于存在占用資源者申請另一個(gè)資源的情形,在DIN中由于拓?fù)浣Y(jié)構(gòu)本身存在環(huán)狀路徑,所以
    發(fā)表于 02-23 14:47 ?892次閱讀
    DIN中的<b class='flag-5'>死鎖</b>避免和<b class='flag-5'>死鎖</b>恢復(fù)

    淺談易用性測試及GUI常見的測試要求

    淺談易用性測試及GUI常見的測試要求
    的頭像 發(fā)表于 06-29 10:15 ?2751次閱讀

    盤點(diǎn)MySQL常見問題及解答

    MySQL常見問題及答案匯總,MySQL是一種開放源代碼的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)。數(shù)據(jù)庫按照數(shù)據(jù)結(jié)構(gòu)來組織、存儲(chǔ)和管理數(shù)據(jù)的倉庫。每個(gè)數(shù)據(jù)庫都有一個(gè)或多個(gè)不同的 API 用于創(chuàng)建,訪問,管理,搜索和復(fù)制所保存的數(shù)據(jù)。
    的頭像 發(fā)表于 01-03 15:25 ?2410次閱讀

    常見MySQL高頻面試題

    在各類技術(shù)崗位面試中,似乎 MySQL 相關(guān)問題經(jīng)常被問到。無論你面試開發(fā)崗位或運(yùn)維崗位,總會(huì)問幾道數(shù)據(jù)庫問題。經(jīng)常有小伙伴私信我,詢問如何應(yīng)對(duì) MySQL 面試題。其實(shí)很多面試題都是大同小異的,提前做準(zhǔn)備還是很有必要的。本篇文章簡單說下幾個(gè)
    的頭像 發(fā)表于 02-08 16:05 ?2328次閱讀

    MySQL中的高級(jí)內(nèi)容詳解

    之前兩篇文章帶你了解了 MySQL 的基礎(chǔ)語法和 MySQL 的進(jìn)階內(nèi)容,那么這篇文章我們來了解一下 MySQL 中的高級(jí)內(nèi)容。 其他文章: 138 張圖帶你 MySQL 入門 47
    的頭像 發(fā)表于 03-11 16:55 ?2151次閱讀
    <b class='flag-5'>MySQL</b>中的高級(jí)內(nèi)容詳解

    MySQL并發(fā)Replace into導(dǎo)致死鎖場景簡析

    在之前的文章 #issue 68021 MySQL unique check 問題中, 我們已經(jīng)介紹了在 MySQL 里面, 由于唯一鍵的檢查(unique check), 導(dǎo)致 MySQL 在 Read Commit 隔離級(jí)別
    的頭像 發(fā)表于 06-13 10:56 ?1177次閱讀
    <b class='flag-5'>MySQL</b>并發(fā)Replace into導(dǎo)致<b class='flag-5'>死鎖</b><b class='flag-5'>場景</b>簡析

    通過GDB non-stop mode調(diào)試MySQL

    通過GDB non-stop mode 調(diào)試MySQL, 特別是用于復(fù)現(xiàn)死鎖場景, 需要按照一定的并發(fā)順序?qū)懭氩趴梢詷?gòu)造出來, 通過GDB non-stop mode 可以非常方便進(jìn)行構(gòu)造
    的頭像 發(fā)表于 09-25 10:34 ?550次閱讀
    通過GDB non-stop mode調(diào)試<b class='flag-5'>MySQL</b>

    Linux內(nèi)核死鎖lockdep功能

    的編程思路,也不可能避免會(huì)發(fā)生死鎖。在Linux內(nèi)核中,常見死鎖有如下兩種: 遞歸死鎖:如在中斷延遲操作中使用了鎖,和外面的鎖構(gòu)成了遞歸死鎖
    的頭像 發(fā)表于 09-27 15:13 ?626次閱讀
    Linux內(nèi)核<b class='flag-5'>死鎖</b>lockdep功能

    死鎖的產(chǎn)生因素

    一、死鎖的概念 操作系統(tǒng)中的死鎖是指: 如果在一個(gè)進(jìn)程集合中的每個(gè)進(jìn)程都在等待只能有該集合中的其它進(jìn)程才能引起的事件,而無限期陷入僵持的局面稱為死鎖。 二、死鎖的產(chǎn)生因素 1、系統(tǒng)擁有
    的頭像 發(fā)表于 11-09 09:37 ?1001次閱讀
    <b class='flag-5'>死鎖</b>的產(chǎn)生因素

    死鎖的現(xiàn)象及原理

    原理 1.1 復(fù)現(xiàn)最簡單的死鎖 線程A占有鎖1,線程B占有鎖2;此時(shí)線程A想要獲取鎖2,但是鎖2已經(jīng)被線程B占有, 此時(shí)線程A會(huì)休眠等待線程B釋放鎖2后,再去獲得鎖2??梢钥吹较旅娴?b class='flag-5'>場景,線程B想要獲取鎖1,結(jié)果線程B也休眠去了。這就導(dǎo)致
    的頭像 發(fā)表于 11-10 16:32 ?398次閱讀
    <b class='flag-5'>死鎖</b>的現(xiàn)象及原理

    死鎖的現(xiàn)象以及原理

    前言 本文將從0到1寫一個(gè)死鎖檢測組件。源碼:deadlock_success.c 組件如何放入自己的項(xiàng)目里?把代碼末兩個(gè)Debug部分刪除,在你的項(xiàng)目里添加下面兩句代碼即可使用死鎖檢測組件
    的頭像 發(fā)表于 11-13 16:30 ?489次閱讀
    <b class='flag-5'>死鎖</b>的現(xiàn)象以及原理

    java死鎖產(chǎn)生的條件

    Java死鎖是指多個(gè)線程因?yàn)榛ハ嗟却龑?duì)方釋放資源而無法繼續(xù)執(zhí)行的情況。當(dāng)線程處于死鎖狀態(tài)時(shí),程序會(huì)無限期地等待資源,無法繼續(xù)執(zhí)行下去,從而導(dǎo)致整個(gè)系統(tǒng)的停滯。要理解并避免Java死鎖的產(chǎn)生,首先需要
    的頭像 發(fā)表于 12-04 13:42 ?407次閱讀