一 、基本概念
InnoDB支持幾種不同的行級鎖,MyISAM只支持表級鎖
行鎖(Record Lock): 對索引記錄加鎖。
間隙鎖(Gap Lock): 鎖住整個區(qū)間,包括:區(qū)間里具體的索引記錄,不存在的空閑空間(可以是兩個索引記錄之間,也可能是第一個索引記錄之前或最后一個索引記錄之后的空間)。
next-key鎖: 行鎖和間隙鎖組合起來。
注意:如果檢索條件不是索引的話會全表掃描,則是表級鎖,不是行級鎖
二、行鎖(Record Lock)
對主鍵或者唯一索引進行增刪改或顯示的加鎖,InnoDB會加行鎖,如:
-- 顯示的加鎖
select * from people where id =3 for update;
update people set name='James' where id=3
注意:
- 正常的查詢語句使用的是共享鎖。
- 對于顯示的加鎖或增刪改操作,條件判斷必須是精確匹配(也就是=) ,不能用>,<,between或like等范圍查詢方式,因為這樣會使行鎖變成next-key Lock。
三、間隙鎖(Gap Lock)
官方文檔描述:Gap Lock的唯一目的就是阻止其他事務(wù)插入到間隙中。Gap Lock可以同時存在,不同的事務(wù)可以同時獲取相同的Gap Lock,并不會互相沖突。Gap Lock也是可以顯示的被禁止的,只要將事務(wù)的隔離級別降低到 READ COMMITTED。
對于間隙鎖,什么叫鎖住不存在的空閑空間,舉個例子:
一個表有id為1,2,3,5,6,9行數(shù)據(jù),執(zhí)行如下sql語句
select * from people where id > 3 AND id <7 for update;
這是一個范圍檢索,InnoDB不僅會鎖住id為5和6兩行的數(shù)據(jù),也會鎖住id為4(雖然該行并不存在)的紀錄。
四、next-key Lock
官方文檔描述:Record Lock+Gap Lock,如果一個事務(wù)在記錄R上的某個索引有共享/互斥鎖,也會對其前面一個范圍加鎖
鎖定的區(qū)域
根據(jù)索引會形成一個個左開右閉的一個區(qū)間,根據(jù)查詢的條件其所在的區(qū)間,并且包括其后的區(qū)間。
這里給出一個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;
那么會鎖住(28,37]這么范圍
如果執(zhí)行如下語句:
select * from people where age =33 for update;
那么會鎖住(28,34)這么范圍
間隙鎖的目的是為了防止幻讀,其主要通過兩個方面實現(xiàn)這個目的:
(1)防止間隙內(nèi)有新數(shù)據(jù)被插入
(2)防止已存在的數(shù)據(jù),更新成間隙內(nèi)的數(shù)據(jù)
innodb自動使用間隙鎖的條件:
(1)必須在Repeatable Read級別下
(2)檢索條件必須有普通索引(沒有索引的話,mysql會全表掃描,那樣會鎖定整張表所有的記錄,包括不存在的記錄,此時其他事務(wù)不能修改不能刪除不能添加)
注意:這里的普通索引不包括主鍵索引和唯一索引,如果在這兩個索引下因為能精確檢索出結(jié)果,所以會使用Record Lock直接鎖定具體的行(范圍查詢除外)。
舉例: 對于表people
mysql> select * from people ;
+----+-------+
| id | name |
+----+-------+
| 1 | JAMES |
| 2 | OVEN |
| 3 | LOVE |
+----+-------+
時間順序 | 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; (此操作會被鎖住) |
因為通過name索引執(zhí)行select …for update會將(1,“JAMES”)到(3,“LOVE”)之間的數(shù)據(jù)都鎖住,所以第4步會被鎖住。
但是如果通過id主鍵索引來檢索
時間順序 | 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; (此操作會被執(zhí)行) |
對于間隙鎖的許多例子可以看Mysql中的幾種行鎖(間隙鎖、next-key鎖)
這個博客中的圖片顯示不了,可以通過下面的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
五 、快照讀和當前讀
快照讀: 通過MVCC實現(xiàn),該技術(shù)不僅可以保證innodb的可重復(fù)讀,而且可以防止幻讀。但是他讀取的數(shù)據(jù)雖然是一致的,但是數(shù)據(jù)是歷史數(shù)據(jù)。
簡單的select操作(不包括 select … lock in share mode, select … for update)
當前讀: 要做到保證數(shù)據(jù)是一致的,同時讀取的數(shù)據(jù)是最新的數(shù)據(jù)。innodb提供了next-key lock,即gap鎖與行鎖結(jié)合來實現(xiàn)。
select … lock in share mode
select … for update
insert
update
delete
總結(jié)
在可重復(fù)讀隔離級別下并不能避免幻讀,如果要避免的話需要使用Next-Key Lock。但是有了Next-Key Lock以后,會導(dǎo)致并發(fā)插入的時候產(chǎn)生等待,所以這時候需要進行相關(guān)的優(yōu)化。