眾所周知,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 鎖的是 (-,1),(2,3),(3,7),(7,+
)反正鎖的就是區(qū)間,不是行
next-key lock鎖的是 (-,1],[2,3),[3,7),[ 7,+
)既鎖范圍也鎖行
做實(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ò)






-
間隙鎖的另一種產(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í)別

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










