總體來說,就是MySQL innoDB引擎要在RR隔離級別之下解決幻讀的問題,所以引入了間隙鎖。
在進(jìn)行當(dāng)前讀的情況下,對讀出的數(shù)據(jù)的附近的一整個(gè)范圍(“間隙”)進(jìn)行加鎖,保證滿足查詢條件的記錄不能被插入。
1、幻讀與innoDB的隔離級別(為什么會出現(xiàn)間隙鎖這個(gè)概念)
根據(jù) ISO/ANSI SQL92 所定義的標(biāo)準(zhǔn),四級隔離級別中,只有在可串行化的級別之下,才可以防止幻讀的出現(xiàn)。
所謂幻讀,指的是事務(wù)A執(zhí)行過程中,由于事務(wù)B并發(fā)插入了一條新數(shù)據(jù),事務(wù)A兩次讀數(shù)據(jù)的內(nèi)容不一樣,出現(xiàn)了“虛幻”的新紀(jì)錄(phantom,幽靈)。
但實(shí)際上各廠商的數(shù)據(jù)庫產(chǎn)品并沒有嚴(yán)格遵守SQL92標(biāo)準(zhǔn),比如Oracle只有 RC 和 Serializable 兩級隔離級別。
MySQL的innoDB引擎雖然擁有標(biāo)準(zhǔn)的四級隔離級別(其實(shí)也是MVCC等手段模擬出來的),不過它有一點(diǎn)顯著的不同,就是在 RR 級別下面已經(jīng)可以防止幻讀的發(fā)生。
-
在快照讀(snapshot read)的情況下,MySQL通過MVCC(多版本并發(fā)控制)來避免幻讀。
快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖。主要應(yīng)用于無需加鎖的普通查詢(select)操作。
-
在當(dāng)前讀(current read)的情況下,MySQL通過next-key lock來避免幻讀。
當(dāng)前讀,讀取的是記錄的最新版本,并且會對當(dāng)前記錄加鎖,防止其他事務(wù)發(fā)修改這條記錄。加行共享鎖(SELECT ... LOCK IN SHARE MODE )、加行排他鎖(SELECT ... FOR UPDATE / INSERT / UPDATE / DELETE)的操作都會用到當(dāng)前度。行鎖可參看 MySQL行鎖。
下面主要說明跟間隙鎖相關(guān)的當(dāng)前讀。
2、innoDB的間隙鎖/Next-Key Lock
2-1、明確前提條件
- innoDB的間隙鎖只存在于 RR 隔離級別
所以希望禁用間隙鎖,提升系統(tǒng)性能的時(shí)候,可以考慮將隔離級別降為 RC。
2-2、間隙鎖/Next-Key Lock
間隙鎖在innoDB中的唯一作用就是在一定的“間隙”內(nèi)防止其他事務(wù)的插入操作,以此防止幻讀的發(fā)生:
- 防止間隙內(nèi)有新數(shù)據(jù)被插入。
- 防止已存在的數(shù)據(jù),更新成間隙內(nèi)的數(shù)據(jù)。
innoDB支持三種行鎖定方式:
行鎖(Record Lock):鎖直接加在索引記錄上面(無索引項(xiàng)時(shí)演變成表鎖)。
間隙鎖(Gap Lock):鎖定索引記錄間隙,確保索引記錄的間隙不變。間隙鎖是針對事務(wù)隔離級別為可重復(fù)讀或以上級別的。
Next-Key Lock :行鎖和間隙鎖組合起來就是 Next-Key Lock。
innoDB默認(rèn)的隔離級別是可重復(fù)讀(Repeatable Read),并且會以Next-Key Lock的方式對數(shù)據(jù)行進(jìn)行加鎖。Next-Key Lock是行鎖和間隙鎖的組合,當(dāng)InnoDB掃描索引記錄的時(shí)候,會首先對索引記錄加上行鎖(Record Lock),再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。加上間隙鎖之后,其他事務(wù)就不能在這個(gè)間隙修改或者插入記錄。
當(dāng)查詢的索引含有唯一屬性(唯一索引,主鍵索引)時(shí),Innodb存儲引擎會對next-key lock進(jìn)行優(yōu)化,將其降為record lock,即僅鎖住索引本身,而不是范圍。
2-3、何時(shí)使用行鎖,何時(shí)產(chǎn)生間隙鎖
對上一節(jié)的最后一句做個(gè)擴(kuò)展說明。
- 只使用唯一索引查詢,并且只鎖定一條記錄時(shí),innoDB會使用行鎖。
- 只使用唯一索引查詢,但是檢索條件是范圍檢索,或者是唯一檢索然而檢索結(jié)果不存在(試圖鎖住不存在的數(shù)據(jù))時(shí),會產(chǎn)生 Next-Key Lock。
- 使用普通索引檢索時(shí),不管是何種查詢,只要加鎖,都會產(chǎn)生間隙鎖。
- 同時(shí)使用唯一索引和普通索引時(shí),由于數(shù)據(jù)行是優(yōu)先根據(jù)普通索引排序,再根據(jù)唯一索引排序,所以也會產(chǎn)生間隙鎖。