時隔很久,又開始碼字了,在最近這段時間將會給大家帶來MySQL相關的面試知識點分享,從原理及面試考察點全面剖析、濃縮、提煉面試表達點。
前言
本次分享MySQL間隙鎖及RR隔離級別情況下常見的加鎖分析,那么廢話不多講直接擼起袖子干。
什么是間隙鎖
間隙鎖
OK如上圖所示,MySQL中的間隙鎖(next-key lock)由間隙鎖(gap lock)和行鎖(row lock)組成。在使用過程中,會把查詢范圍的整個區間進行鎖定,咱們可以先這樣理解,后面慢慢剖析。
為什么有間隙鎖
在RR隔離級別下,則有可能出現幻讀(強調新增,前后兩次查詢結果不一致)的情況。所以在MySQL中,加入了間隙鎖對當前讀下的幻讀進行解決。
為什么會出現幻讀
我們來分析一下此時RC隔離級別+b普通索引的加鎖情況:
RC隔離級別下的幻讀
可以看到RC隔離級別下,b=5的記錄加上了X鎖,但是(0,5)還有(5,10)的間隙沒有鎖的情況,所以在這個間隙中可以插入新的數據。
現在我就可以回答出為什么會產生幻讀了。產生幻讀的原因是,行鎖只能鎖住行,但是新插入記錄的這個動作,要更新的是記錄之間的“間隙”。因此,為了解決幻讀的問題,InnoDB在RR隔離級別引入了新的鎖,它就是間隙鎖(Gap Lock)。
RC隔離級別下的加鎖情況
下面進行測試表結構創建:
create table `test`(
`id` int(11) NOT NULL,
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
`c` int(11) NOT NULL,
PRIMARY KEY(`id`),
UNIQUE KEY unix_key('a'),
KEY ix_key(`b`)
)ENGINE=InnoDB;
insert into test values(0,0,0,0),(5,5,5,5),(10,10,10,10),(20,20,20,20);
創建一張test表,id是主鍵,a是唯一索引,b是普通索引,c是沒有索引。
現在假設是在RC隔離級別下,我們來做一個實驗看看:
事務A |
事務B |
set session transaction_isolation='READ-COMMITTED'; |
set session transation_isolation='READ-COMMITTED'; |
begin; |
|
select * from test where b=5 for update; 返回:(5,5,5,5) |
|
|
begin; |
|
insert into test values(6,6,5,5); |
|
commit; |
select * from test where b=5 for update; 返回:(5,5,5,5),(6,6,5,5) |
|
commit; |
由上面實驗得出,在RC隔離級別下,事務A對b=5這一行記錄加了X鎖,但是事務B插入一條新的記錄的b字段也是為5,然后在事務A中可以查出b=5有兩條記錄,這個就產生了“幻讀”。
也就是說,幻讀是指一個事務前后兩次查詢同一范圍的時候,后一次查詢看到了前一次查詢沒有看到的行記錄。
RR隔離級別下的加鎖情況
加鎖分析(以下默認都是RR隔離級別并且都是當前讀)
這里我挑選出RR隔離級別下三種常見的情況進行SQL加鎖分析:
- RR隔離級別,where字段沒有索引
- RR隔離級別,where字段有普通索引
- RR隔離級別,where字段有唯一索引
(1)RR隔離級別+無索引
RR隔離級別+無索引
上圖所示,GAP鎖加在c字段的(負無窮,0),(0,5),(5,10),(10,20),(20,正無窮)。但是在RR隔離級別下,我們都是默認是用next-key Lock(行鎖+間隙鎖),所以我們都是默認是左開右閉,同時也得知了所有的記錄都加上了X鎖及GAP鎖(可以理解成表鎖)。因此這張表在執行該SQL期間并未commit的話,除了不加鎖的快照讀事務,其他任何加鎖的操作都將堵塞,如果是線上環境,將會是件非常有意思的事。
總結一下:RR隔離級別下,無索引的條件字段的當前讀不僅會把每條記錄都加上X鎖,還會加上GAP鎖。
(2)RR隔離級別+唯一索引
唯一索引的情況是最簡單的,因為不管是RC隔離級別或者是RR隔離級別,唯一索引都只能查出一條記錄,只會在對應的行記錄加上X鎖就沒了。
為什么會這樣?
因為GAP鎖的目的是為了防止同一事務被連續兩次當前讀,然后兩次讀的情況不一致。如果能夠保證字段是唯一的(唯一索引),其實就是最多只有一條記錄滿足條件,所以查詢唯一索引的時候絕對不會出現GAP鎖。
可以理解為RR+唯一索引和RC+唯一索引的加鎖情況是一樣的就好了。
(3)RR隔離級別+普通索引
RR隔離級別+普通索引
如上圖所示,普通索引字段b=10給兩條記錄加了X鎖,并且把聚集索引樹的兩條記錄也加了X鎖。GAP鎖的是b的范圍(5,10),(10,正無窮)。所以next-key Lock鎖的是(5,10],(10,10],(10,正無窮]。期間只要是b在next-key Lock的范圍內就更新全部阻塞。
舉例:insert into test values(6,6,6,6)就會被阻塞,原因就是RR隔離級別的間隙鎖鎖住了記錄之間的“間隙”,所以會阻塞
總結
- 這次分享了RC隔離級別下出現幻讀的情況,然后分析了為什么會出現幻讀。
- InnoDB為了解決幻讀,在RR下引入了GAP鎖,和行鎖組成next-key Lock
- 分析了三種常見情況的加鎖情況操作。