mysql間隙鎖和行鎖

一 、基本概念

InnoDB支持幾種不同的行級(jí)鎖,MyISAM只支持表級(jí)鎖
行鎖(Record Lock): 對(duì)索引記錄加鎖。
間隙鎖(Gap Lock): 鎖住整個(gè)區(qū)間,包括:區(qū)間里具體的索引記錄,不存在的空閑空間(可以是兩個(gè)索引記錄之間,也可能是第一個(gè)索引記錄之前或最后一個(gè)索引記錄之后的空間)。
next-key鎖: 行鎖和間隙鎖組合起來。

注意:如果檢索條件不是索引的話會(huì)全表掃描,則是表級(jí)鎖,不是行級(jí)鎖

二、行鎖(Record Lock)

對(duì)主鍵或者唯一索引進(jìn)行增刪改或顯示的加鎖,InnoDB會(huì)加行鎖,如:

-- 顯示的加鎖
select * from  people where id =3 for update;

update people set name='James' where id=3

注意:

  1. 正常的查詢語句使用的是共享鎖。
  2. 對(duì)于顯示的加鎖或增刪改操作,條件判斷必須是精確匹配(也就是=) ,不能用>,<,between或like等范圍查詢方式,因?yàn)檫@樣會(huì)使行鎖變成next-key Lock。
三、間隙鎖(Gap Lock)

官方文檔描述:Gap Lock的唯一目的就是阻止其他事務(wù)插入到間隙中。Gap Lock可以同時(shí)存在,不同的事務(wù)可以同時(shí)獲取相同的Gap Lock,并不會(huì)互相沖突。Gap Lock也是可以顯示的被禁止的,只要將事務(wù)的隔離級(jí)別降低到 READ COMMITTED。

對(duì)于間隙鎖,什么叫鎖住不存在的空閑空間,舉個(gè)例子:
一個(gè)表有id為1,2,3,5,6,9行數(shù)據(jù),執(zhí)行如下sql語句

select * from  people where id > 3 AND id <7 for update;

這是一個(gè)范圍檢索,InnoDB不僅會(huì)鎖住id為5和6兩行的數(shù)據(jù),也會(huì)鎖住id為4(雖然該行并不存在)的紀(jì)錄。

四、next-key Lock

官方文檔描述:Record Lock+Gap Lock,如果一個(gè)事務(wù)在記錄R上的某個(gè)索引有共享/互斥鎖,也會(huì)對(duì)其前面一個(gè)范圍加鎖

鎖定的區(qū)域
根據(jù)索引會(huì)形成一個(gè)個(gè)左開右閉的一個(gè)區(qū)間,根據(jù)查詢的條件其所在的區(qū)間,并且包括其后的區(qū)間。

這里給出一個(gè)people表

id name age
1 JAMES 37
2 OVEN 28
3 LOVE 34

如果age是索引的話,相關(guān)的區(qū)域有
(-無窮,28]
(28,34]
(34,37]
(37,+無窮)

如果執(zhí)行如下語句:

select * from  people where age =34 for update;

那么會(huì)鎖住(28,37]這么范圍

如果執(zhí)行如下語句:

select * from  people where age =33 for update;

那么會(huì)鎖住(28,34)這么范圍

間隙鎖的目的是為了防止幻讀,其主要通過兩個(gè)方面實(shí)現(xiàn)這個(gè)目的:
(1)防止間隙內(nèi)有新數(shù)據(jù)被插入
(2)防止已存在的數(shù)據(jù),更新成間隙內(nèi)的數(shù)據(jù)

innodb自動(dòng)使用間隙鎖的條件
(1)必須在Repeatable Read級(jí)別下
(2)檢索條件必須有普通索引(沒有索引的話,mysql會(huì)全表掃描,那樣會(huì)鎖定整張表所有的記錄,包括不存在的記錄,此時(shí)其他事務(wù)不能修改不能刪除不能添加)

注意:這里的普通索引不包括主鍵索引和唯一索引,如果在這兩個(gè)索引下因?yàn)槟芫_檢索出結(jié)果,所以會(huì)使用Record Lock直接鎖定具體的行(范圍查詢除外)。

舉例: 對(duì)于表people

mysql> select * from people ;
+----+-------+
| id | name  |
+----+-------+
|  1 | JAMES |
|  2 | OVEN  |
|  3 | LOVE  |
+----+-------+

時(shí)間順序 session A session B
1 Begin;
2 Begin;
3 select * from people where name=“OVEN” for update ;(查詢到“OVEN”)
4 update people set name=“zph” where id=1; (此操作會(huì)被鎖住)

因?yàn)橥ㄟ^name索引執(zhí)行select …for update會(huì)將(1,“JAMES”)到(3,“LOVE”)之間的數(shù)據(jù)都鎖住,所以第4步會(huì)被鎖住。

但是如果通過id主鍵索引來檢索

時(shí)間順序 session A session B
1 Begin;
2 Begin;
3 select * from people where id=2 for update ;(查詢到“OVEN”)
4 update people set name=“zph” where id=1; (此操作會(huì)被執(zhí)行)

對(duì)于間隙鎖的許多例子可以看Mysql中的幾種行鎖(間隙鎖、next-key鎖)

這個(gè)博客中的圖片顯示不了,可以通過下面的url訪問
https://img-blog.csdnimg.cn/20200524231954401.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NzUxMzIw,size_16,color_FFFFFF,t_70

五 、快照讀和當(dāng)前讀

快照讀: 通過MVCC實(shí)現(xiàn),該技術(shù)不僅可以保證innodb的可重復(fù)讀,而且可以防止幻讀。但是他讀取的數(shù)據(jù)雖然是一致的,但是數(shù)據(jù)是歷史數(shù)據(jù)。

簡(jiǎn)單的select操作(不包括 select … lock in share mode, select … for update)

當(dāng)前讀: 要做到保證數(shù)據(jù)是一致的,同時(shí)讀取的數(shù)據(jù)是最新的數(shù)據(jù)。innodb提供了next-key lock,即gap鎖與行鎖結(jié)合來實(shí)現(xiàn)。

select … lock in share mode

select … for update

insert

update

delete

總結(jié)

在可重復(fù)讀隔離級(jí)別下并不能避免幻讀,如果要避免的話需要使用Next-Key Lock。但是有了Next-Key Lock以后,會(huì)導(dǎo)致并發(fā)插入的時(shí)候產(chǎn)生等待,所以這時(shí)候需要進(jìn)行相關(guān)的優(yōu)化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容