MySQL的行級(jí)鎖鎖的到底是什么?
往期在文章《介紹Innodb的鎖機(jī)制》中提到過關(guān)于記錄鎖,但是沒有詳細(xì)展開描述。本片文章簡(jiǎn)單聊一聊。
數(shù)據(jù)庫的行級(jí)鎖,隨著鎖的細(xì)粒度不同,擁有不同的命名。
- 記錄鎖(Record Lock)指的是對(duì)索引記錄的鎖定。
- 間隙鎖(Gap Lock)則是對(duì)索引記錄之間的間隙進(jìn)行鎖定。
而Next-Key Lock則是記錄鎖和間隙鎖的融合,同時(shí)鎖定索引記錄和間隙。其范圍為左開右閉。
什么是Record Lock
記錄鎖,即Record Lock,是針對(duì)索引記錄而言的鎖定。例如,執(zhí)行以下語句:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 會(huì)對(duì)滿足條件c1=10的記錄進(jìn)行鎖定,以防止其他任何事務(wù)插入、更新或刪除具有相同c1值的行。
什么是Gap Lock
間隙鎖,即Gap Lock,指的是針對(duì)索引記錄之間的間隙,或者是在第一個(gè)索引記錄之前或最后一個(gè)索引記錄之后的空隙上的鎖定。
在這里,所謂的“間隙”是指InnoDB索引數(shù)據(jù)結(jié)構(gòu)中可供插入新值的位置。
當(dāng)你使用SELECT…FOR UPDATE語句鎖定一組行時(shí),InnoDB可以創(chuàng)建鎖,應(yīng)用于索引中的實(shí)際值以及它們之間的間隙。例如,如果你選擇更新所有大于10的值,間隙鎖將阻止另一個(gè)事務(wù)插入新的大于10的值。
(實(shí)際會(huì)鎖到+∞,這里為了演示什么是gap簡(jiǎn)化了一下)
由于鎖的存在可能影響數(shù)據(jù)庫的并發(fā)性,因此間隙鎖只在Repeatable Reads(可重復(fù)讀)這種隔離級(jí)別下才會(huì)發(fā)揮作用。
在Repeatable Reads隔離級(jí)別下,針對(duì)鎖定的讀操作(例如select ... for update、lock in share mode)、update操作和delete操作,會(huì)執(zhí)行以下加鎖操作:
- 對(duì)于具有唯一搜索條件的唯一索引,InnoDB僅鎖定找到的索引記錄,而不鎖定間隙。
- 對(duì)于其他搜索條件,InnoDB會(huì)鎖定掃描的索引范圍,并使用間隙鎖或next-key鎖來阻止其他事務(wù)插入范圍內(nèi)的間隙。
換句話說,在處理**SELECT FOR UPDATE、LOCK IN SHARE MODE、UPDATE和DELETE**等語句時(shí),除了對(duì)具有唯一搜索條件的唯一索引外,還會(huì)獲取間隙鎖或next-key鎖,即鎖定其掃描的范圍。
什么是Next-Key Lock
Next-Key鎖是指索引記錄上的記錄鎖和索引記錄之間間隙上的間隙鎖的結(jié)合。
假設(shè)一個(gè)索引包含值10、11、13和20。此索引可能的next-key鎖包括以下區(qū)間:
(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, ∞ ]
對(duì)于最后一個(gè)間隙,∞并不是一個(gè)真正的索引記錄,因此,實(shí)際上,這個(gè)next-key鎖只鎖定最大索引值之后的間隙。
因此,Next-Key鎖的范圍都是左開右閉的。
與Gap Lock一樣,Next-Key Lock只有在InnoDB的可重復(fù)讀(RR)隔離級(jí)別中才會(huì)生效。
談?wù)凪ySQL加鎖機(jī)制
根據(jù)丁奇大佬《MySQL實(shí)戰(zhàn)45講》中的總結(jié),加鎖規(guī)則可以歸納為兩個(gè)“原則”、兩個(gè)“優(yōu)化”和一個(gè)“bug”:
- 原則 1:加鎖的基本單位是next-key lock,形成一個(gè)前開后閉的區(qū)間。
- 原則 2:只有查找過程中訪問到的對(duì)象才會(huì)被加鎖。
- 優(yōu)化 1:對(duì)于索引上的等值查詢,當(dāng)給唯一索引加鎖時(shí),next-key lock會(huì)退化為行鎖。
- 優(yōu)化 2:對(duì)于索引上的等值查詢,在向右遍歷時(shí),且最后一個(gè)值不滿足等值條件時(shí),next-key lock會(huì)退化為間隙鎖。
- 一個(gè)bug:唯一索引上的范圍查詢會(huì)一直訪問到不滿足條件的第一個(gè)值為止。
當(dāng)我們執(zhí)行update t set d=d+1 where id = 7的時(shí)候,由于表 t 中沒有 id=7 的記錄,所以:
- 根據(jù)原則 1,加鎖單位是 next-key lock,session A 加鎖范圍就是 (5,10];
- 根據(jù)優(yōu)化 2,這是一個(gè)等值查詢 (id=7),而 id=10 不滿足查詢條件,next-key lock 退化成間隙鎖,因此最終加鎖的范圍是 (5,10)。
當(dāng)我們執(zhí)行select * from t where id>=10 and id<11 for update的時(shí)候:
- 根據(jù)原則 1,加鎖單位是 next-key lock,會(huì)給 (5,10]加上 next-key lock,范圍查找就往后繼續(xù)找,找到 id=15 這一行停下來
- 根據(jù)優(yōu)化 1,主鍵 id 上的等值條件,退化成行鎖,只加了 id=10 這一行的行鎖。
- 根據(jù)原則 2,訪問到的都要加鎖,因此需要加 next-key lock(10,15]。因此最終加的是行鎖 id=10 和 next-key lock(10,15]。
當(dāng)執(zhí)行 select * from t where id>10 and id<=15 for update 時(shí):
- 根據(jù)原則 1,加鎖單位是 next-key lock,會(huì)給 (10,15] 加上 next-key lock,并且由于 id 是唯一鍵,所以應(yīng)該循環(huán)判斷到 id=15 這一行就停止。
- 但是,InnoDB 實(shí)際上會(huì)往前掃描到第一個(gè)不滿足條件的行,即 id=20。由于這是一個(gè)范圍掃描,因此索引 id 上的 (15,20] 這個(gè) next-key lock 也會(huì)被鎖上。
假如,數(shù)據(jù)庫表中當(dāng)前有以下記錄:
當(dāng)執(zhí)行 select id from t where c=5 lock in share mode 時(shí):
- 根據(jù)原則 1,加鎖單位是 next-key lock,因此會(huì)給 (0,5] 加上 next-key lock。需要注意的是,c 是普通索引,因此不能立即停止于 c=5 這一條記錄,需要向右遍歷,直到找到 c=10 才放棄。
- 根據(jù)原則 2,訪問到的都要加鎖,因此要給 (5,10] 加上 next-key lock。
- 根據(jù)優(yōu)化 2:等值判斷,向右遍歷,最后一個(gè)值不滿足 c=5 這個(gè)等值條件,因此退化成間隙鎖 (5,10)。
- 根據(jù)原則 2,只有訪問到的對(duì)象才會(huì)加鎖。由于這個(gè)查詢使用了覆蓋索引,不需要訪問主鍵索引,所以在主鍵索引上沒有加任何鎖。
當(dāng)執(zhí)行 select * from t where c>=10 and c<11 for update 時(shí):
- 根據(jù)原則 1,加鎖單位是 next-key lock,會(huì)給 (5,10] 加上 next-key lock,并繼續(xù)向后查找,直到找到 id=15 這一行停止。
- 根據(jù)原則 2,訪問到的都要加鎖,因此需要加 next-key lock (10,15]。
- 由于索引 c 是非唯一索引,沒有優(yōu)化規(guī)則,也就是說不會(huì)退化為行鎖,因此最終 session A 加的鎖是,索引 c 上的 (5,10] 和 (10,15] 這兩個(gè) next-key lock。
結(jié)語
以上,我們介紹了InnoDB中的鎖機(jī)制,一共有三種鎖,分別是Record Lock、Gap Lock和Next-Key Lock。
Record Lock表示記錄鎖,鎖的是索引記錄。Gap Lock是間隙鎖,說的是索引記錄之間的間隙。Next-Key Lock是Record Lock和Gap Lock的組合,同時(shí)鎖索引記錄和間隙。他的范圍是左開右閉的。
InnoDB的RR級(jí)別中,加鎖的基本單位是 next-key lock,只要掃描到的數(shù)據(jù)都會(huì)加鎖。唯一索引上的范圍查詢會(huì)訪問到不滿足條件的第一個(gè)值為止。
同時(shí),為了提升性能和并發(fā)度,也有兩個(gè)優(yōu)化點(diǎn):
- 索引上的等值查詢,給唯一索引加鎖的時(shí)候,next-key lock 退化為行鎖。
- 索引上的等值查詢,向右遍歷時(shí)且最后一個(gè)值不滿足等值條件的時(shí)候,next-key lock 退化為間隙鎖。
關(guān)于鎖的介紹,就是這么多了,但是其實(shí),RR的隔離級(jí)別引入的這些鎖,雖然一定程度上可解決很多如幻讀這樣的問題,但是也會(huì)帶來一些副作用,比如并發(fā)度降低、容易導(dǎo)致死鎖等。