間隙鎖(Gap Lock)是Innodb在提交下為了解決幻讀問題時(shí)引入的鎖機(jī)制,(下面的所有案例沒有特意強(qiáng)調(diào)都使用可重復(fù)讀隔離級(jí)別)幻讀的問題存在是因?yàn)樾略龌蛘吒虏僮餮坠Γ@時(shí)如果進(jìn)行范圍查詢的時(shí)候(加鎖查詢)衔瓮,會(huì)出現(xiàn)不一致的問題,這時(shí)使用不同的行鎖已經(jīng)沒有辦法滿足要求移袍,需要對(duì)一定范圍內(nèi)的數(shù)據(jù)進(jìn)行加鎖爷绘,間隙鎖就是解決這類問題的书劝。在可重復(fù)讀隔離級(jí)別下,數(shù)據(jù)庫是通過行鎖和間隙鎖共同組成的(next-key lock)土至,來實(shí)現(xiàn)的
加鎖規(guī)則有以下特性购对,我們會(huì)在后面的案例中逐一解釋:
- 1.加鎖的基本單位是(next-key lock),他是前開后閉原則
- 2.插敘過程中訪問的對(duì)象會(huì)增加鎖
- 3.索引上的等值查詢--給唯一索引加鎖的時(shí)候,next-key lock升級(jí)為行鎖
- 4.索引上的等值查詢--向右遍歷時(shí)最后一個(gè)值不滿足查詢需求時(shí)陶因,next-key lock 退化為間隙鎖
- 5.唯一索引上的范圍查詢會(huì)訪問到不滿足條件的第一個(gè)值為止
案例數(shù)據(jù)
id(主鍵) | c(普通索引) | d(無索引) |
---|---|---|
5 | 5 | 5 |
10 | 10 | 10 |
15 | 15 | 15 |
20 | 20 | 20 |
25 | 25 | 25 |
以上數(shù)據(jù)為了解決幻讀問題骡苞,更新的時(shí)候不只是對(duì)上述的五條數(shù)據(jù)增加行鎖,還對(duì)于中間的取值范圍增加了6間隙鎖楷扬,(-∞解幽,5](5,10](10烘苹,15](15躲株,20](20,25](25镣衡,+supernum] (其中supernum是數(shù)據(jù)庫維護(hù)的最大的值霜定。為了保證間隙鎖都是左開右閉原則。)
案例一:間隙鎖簡(jiǎn)單案例
步驟 | 事務(wù)A | 事務(wù)B |
---|---|---|
1 | begin; select * from t where id = 11 for update; |
- |
2 | - | insert into user value(12,12,12) |
3 | commit; | - |
當(dāng)有如下事務(wù)A和事務(wù)B時(shí)廊鸥,事務(wù)A會(huì)對(duì)數(shù)據(jù)庫表增加(10望浩,15]這個(gè)區(qū)間鎖,這時(shí)insert id = 12 的數(shù)據(jù)的時(shí)候就會(huì)因?yàn)閰^(qū)間鎖(10惰说,15]而被鎖住無法執(zhí)行磨德。
案例二: 間隙鎖死鎖問題
步驟 | 事務(wù)A | 事務(wù)B |
---|---|---|
1 | begin; select * from t where id = 9 for update; |
- |
2 | - | begin; select * from t where id = 6 for update; |
3 | - | insert into user value(7,7,7) |
4 | insert into user value(7,7,7) |
- |
不同于寫鎖相互之間是互斥的原則,間隙鎖之間不是互斥的助被,如果一個(gè)事務(wù)A獲取到了(5,10]之間的間隙鎖剖张,另一個(gè)事務(wù)B也可以獲取到(5,10]之間的間隙鎖切诀。這時(shí)就可能會(huì)發(fā)生死鎖問題揩环,如下案例。
事務(wù)A獲取到(5,10]之間的間隙鎖不允許其他的DDL操作幅虑,在事務(wù)提交丰滑,間隙鎖釋放之前,事務(wù)B也獲取到了間隙鎖(5,10],這時(shí)兩個(gè)事務(wù)就處于死鎖狀態(tài)
案例三: 等值查詢—唯一索引
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; update u set d= d+ 1 where id = 7; |
- | - |
2 | - | insert into u (8,8,8); |
- |
4 | - | - | update set d = d+ 1 where id = 10 |
1.加鎖的范圍是(5,10]的范圍鎖
2.由于數(shù)據(jù)是等值查詢褒墨,并且表中最后數(shù)據(jù)id = 10 不滿足id= 7的查詢要求炫刷,故id=10 的行級(jí)鎖退化為間隙鎖,(5,10)
3.所以事務(wù)B中id=8會(huì)被鎖住郁妈,而id=10的時(shí)候不會(huì)被鎖住
案例四: 等值查詢—普通索引
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; select id form t where c = 5 lock in share mode; |
- | - |
2 | - | update t set d = d + 1 where id = 5 | - |
4 | - | - | insert into values (7,7,7) |
1.加鎖的范圍是(0,5]浑玛,(5,10]的范圍鎖
2.由于c是普通索引,根據(jù)原則4噩咪,搜索到5后繼續(xù)向后遍歷直到搜索到10才放棄顾彰,故加鎖范圍為(5,10]
3.由于查詢是等值查詢,并且最后一個(gè)值不滿足查詢要求胃碾,故間隙鎖退化為(5,10)
4.因?yàn)榧渔i是對(duì)普通索引c加鎖涨享,而且因?yàn)樗饕采w,沒有對(duì)主鍵進(jìn)行加鎖仆百,所以事務(wù)B執(zhí)行正常
5.因?yàn)榧渔i范圍(5,10)故事務(wù)C執(zhí)行阻塞
6.需要注意的是厕隧,lock in share mode 因?yàn)楦采w索引故沒有鎖主鍵索引,如果使用for update 程序會(huì)覺得之后會(huì)執(zhí)行更新操作故會(huì)將主鍵索引一同鎖住
案例五: 范圍查詢—唯一索引
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; select * form t where id >= 10 and id <11 for update |
- | - |
2 | - | insert into values(8,8,8) insert into values(13,13,13) |
- |
4 | - | - | update t set d = d+ 1 where id = 15 |
- next-key lock 增加范圍鎖(5,10]
- 根據(jù)原則5俄周,唯一索引的范圍查詢會(huì)到第一個(gè)不符合的值位置吁讨,故增加(10,15]
3.因?yàn)榈戎挡樵冇衖d =10 根據(jù)原則3間隙鎖升級(jí)為行鎖峦朗,故剩余鎖[10,15]
4.因?yàn)椴樵儾⒉皇堑戎挡樵兊簿簦蔥10,15]不會(huì)退化成[10,15)
5.故事務(wù)B(13,13,13)阻塞,事務(wù)C阻塞
案例六: 范圍查詢—普通索引
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; select * form t where c >= 10 and c <11 for update |
- | - |
2 | - | insert into values(8,8,8) |
- |
4 | - | - | update t set d = d+ 1 where c = 15 |
- next-key lock 增加范圍鎖(5,10]甚垦,(10茶鹃,15]
2.因?yàn)閏是非唯一索引,故(5,10]不會(huì)退化為10
3.因?yàn)椴樵儾⒉皇堑戎挡樵兗枇粒蔥10,15]不會(huì)退化成[10,15)
4.所以事務(wù)B和事務(wù)C全部堵塞
案例八: 普通索引-等值問題
上面的數(shù)據(jù)增加一行(30,10,30)闭翩,這樣在數(shù)據(jù)庫中存在的c=10的就有兩條記錄
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; delete from t where c = 10 |
- | - |
2 | - | insert into values(12,12,12) |
- |
4 | - | - | update t set d = d+ 1 where c = 15 |
- next-key lock 增加范圍鎖(5,10],(10迄埃,15]
2.因?yàn)槭堑戎挡樵児释嘶癁椋?,10]疗韵,(10,15)侄非,故事務(wù)B阻塞蕉汪,事務(wù)C執(zhí)行成功
加鎖的范圍如下圖
案例九: 普通索引-等值Limit問題
步驟 | 事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|---|
1 | begin; delete from t where c = 10 limit 2 |
- | - |
2 | - | insert into values(12,12,12) |
- |
4 | - | - | update t set d = d+ 1 where c = 15 |
1.根據(jù)上面案例8改造,將delete增加limit操作2的操作
2.因?yàn)橹懒藬?shù)據(jù)加鎖值加2條逞怨,故在加鎖(5者疤,10]之后發(fā)現(xiàn)已經(jīng)有兩條數(shù)據(jù),故后面不在向后匹配加鎖叠赦。所以事務(wù)B執(zhí)行成功驹马,加鎖范圍如下