MySQL中innodb的行鎖算法

眾所周知,innodb是默認(rèn)行鎖,當(dāng)然也支持表鎖。如下是對(duì)于行鎖的算法進(jìn)行的一些實(shí)驗(yàn)。

鎖的算法為:我知道是行鎖,但是是如何鎖的,鎖多少數(shù)據(jù)

行鎖算法分類
  • record lock: 鎖一行
  • gap lock :鎖兩個(gè)索引之間的區(qū)間
  • next-key lock record lock + gap lock 既鎖行,也鎖范圍
    示意圖:


    圖片來自掘金

假如有個(gè)索引是:[1,2,3,7]
record lock 鎖的是 1,2,3,7
gap lock 鎖的是 (-\infty,1),(2,3),(3,7),(7,+\infty)反正鎖的就是區(qū)間,不是行
next-key lock鎖的是 (-\infty,1],[2,3),[3,7),[ 7,+\infty)既鎖范圍也鎖行

做實(shí)驗(yàn)的表如下:
CREATE TABLE `t2` (
   `id` int(11) NOT NULL,
   `a` int(11) DEFAULT NULL,
   `b` int(11) DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `a` (`a`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

Innodb鎖算法規(guī)則如下:

  • 不加索引的查詢,會(huì)鎖整張表


    image.png

    image.png
  • innodb通過索引來鎖定數(shù)據(jù)。

  • 當(dāng)數(shù)據(jù)庫操作使用了主鍵索引,則鎖主鍵索引,使用輔助索引,則先鎖住輔助索引,再鎖住主鍵索引。

  • 當(dāng)查詢的是唯一索引的時(shí)候(注意這里的條件是等值查詢,假如是范圍查詢依然是next-key lock 下文有講),next-key lock 會(huì)退化為record lock

  • innodb對(duì)于輔助索引有特殊的處理,不僅會(huì)鎖住輔助索引所在的范圍,還會(huì)鎖住下一鍵值的gap lock.

  • innodb使用next-key lock 來解決幻讀

innodb到底使用什么鎖算法

在可重復(fù)讀隔離級(jí)別下,innodb默認(rèn)使用的是next-key lock算法,當(dāng)查詢的索引是主鍵或者唯一索引的情況下,才會(huì)退化為record lock,在使用next-key lock算法時(shí),不僅僅會(huì)鎖住范圍,還會(huì)給范圍最后的一個(gè)鍵值加一個(gè)gap lock。

實(shí)驗(yàn)

  • 使用主鍵索引會(huì)退化為record lock


    image.png

    僅僅鎖主鍵ID=1的這一行,不影響其他操作,但是查id=1的則會(huì)鎖等待


    image.png

    image.png

其中l(wèi)ockmode中的X鎖為左邊會(huì)話中的鎖,因?yàn)樾枰@式的commit之后才會(huì)釋放鎖,第二個(gè)S鎖,為右邊的共享鎖,因?yàn)橹麈IID為1的已經(jīng)被鎖住了,所以處于鎖等待狀態(tài),鎖的類型為record lock

  • 使用輔助索引會(huì)先鎖輔助索引,再鎖主鍵索引,且鎖算法為next-key lock 且需要對(duì)下一個(gè)鍵值加gap lock


    image.png

    image.png

使用輔助索引a=8進(jìn)行操作,這個(gè)時(shí)候理論應(yīng)該對(duì)主鍵索引加record lock 則 主鍵ID=8的被鎖,然后輔助索引被加next-key lock 則為:
(7,8] 然后對(duì)下一個(gè)鍵值加gap鎖,則為:(8,11)
所以目前被鎖住的記錄為:
1.主鍵為8的被鎖
2.輔助索引8的被鎖
3.輔助索引8到11之間的被鎖,意味著你這個(gè)時(shí)候往8到11之間寫數(shù)據(jù)會(huì)報(bào)錯(cuò)


主鍵查詢被阻塞,意味著主鍵被鎖

輔助索引為8的被阻塞

插入輔助索引為10的被鎖,意味著gap lock鎖住了8到11之間的數(shù)據(jù)了

但是查詢表,此時(shí)的lockdata為11.11,這一點(diǎn)不太明白

插入12的值則順利插入

查詢輔助索引a=3的順利查詢,因?yàn)閍=3的記錄,既沒有被主鍵id=8的鎖住,也沒有被輔助索引a的next-key lock 和gap lock鎖住
  • 間隙鎖的另一種產(chǎn)生方式:


    image.png

    image.png

    image.png

當(dāng)使用范圍條件進(jìn)行更新時(shí),此時(shí)肯定是需要加X鎖的,我是用的也是主鍵,所以按照理論應(yīng)該是加的record lock ,但是卻加了gap lock,因?yàn)椴迦胫禐?0的阻塞了,查看information 也提示X.GAP
這個(gè)有點(diǎn)暈為啥主鍵變成了next-key lock ,不應(yīng)該是record lock么?
update20200515
在知乎看到的一個(gè)解釋:

當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會(huì)對(duì)這個(gè)“間隙”加鎖,這種鎖機(jī)制就是所謂的間隙鎖(Next-Key鎖)

即,在無論使用主鍵索引還是非主鍵索引的時(shí)候,請(qǐng)求共享鎖或者排他鎖,innodb會(huì)給范圍內(nèi)的記錄加鎖,而范圍內(nèi)的間隙也會(huì)被加鎖,
例如一個(gè)表t 的 id為1,2,3,7,10
假如執(zhí)行如下:
select * from t where id >=3 for update
那么這個(gè)時(shí)候執(zhí)行
insert into t(id) values(8) 會(huì)被阻塞,因?yàn)槭窃谡?qǐng)求排他鎖時(shí)使用了范圍,所以[3,10],甚至10以后的任何數(shù)據(jù)都無法插入。
執(zhí)行
select * from t where id >=3 lock in share mode
insert into t(id) values(8) 會(huì)被阻塞,因?yàn)槭窃谡?qǐng)求共享鎖時(shí)使用了范圍,所以[3,10],甚至10以后的任何數(shù)據(jù)都無法插入。

next-key lock可以解決幻讀問題:

什么是幻讀

幻讀是同一事務(wù)下,連續(xù)執(zhí)行兩次同樣的sql可能導(dǎo)致不同的結(jié)果,第二次返回的數(shù)據(jù)可能導(dǎo)致以前不存在的行。
同時(shí)一般會(huì)問它和臟讀的區(qū)別,臟讀為讀取到其他事務(wù)未提交的數(shù)據(jù),但是幻讀是讀取的其他事務(wù)已經(jīng)提交的數(shù)據(jù)。

復(fù)現(xiàn)幻讀:
復(fù)現(xiàn)幻讀需要切換到讀提交的隔離級(jí)別

image.png

假如是可重復(fù)讀隔離級(jí)別,則會(huì)對(duì)[8,+)進(jìn)行加鎖,則10就不會(huì)被寫進(jìn)去。

reference:

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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