每一個數據庫幾乎都會實現本身的鎖機制,鎖機制是數據庫區別於文件系統的主要標誌之一,用於管理對共享資源的併發訪問。 html
Mysql數據庫InnoDB引擎支持行級鎖,也就是說咱們能夠對錶中某些行數據執行鎖定操做,鎖定操做的影響是:若是一個事物對錶中某行執行了鎖定操做,而另外一個事務也須要對一樣的行執行鎖定操做,這樣第二個事務的鎖定操做有可能被阻塞,一旦被阻塞第二個事務只能等到第一個事務執行完畢(提交或回滾)或超時。mysql
本文主要介紹InnoDB中的行鎖相關概念,重點介紹行鎖的鎖定範圍:sql
上面咱們簡單的介紹了InnoDB的行級鎖,爲了理解後面的驗證部分,須要補充一下背景知識。若是對相應知識很是瞭解,能夠直接跳轉到驗證部份內容。數據庫
InnoDB引擎使用了七種類型的鎖,他們分別是:編程
本文主要涉及Shared and Exclusive Locks,Record Locks,Gap Locks,Next-Key Locks這幾種鎖,其餘類型鎖若是你們感興趣能夠本身深刻了解,在此不在詳述。併發
共享鎖(S鎖)和排他鎖(X鎖)的概念在許多編程語言中都出現過。先來描述一下這兩種鎖在MySQL中的影響結果:編程語言
用一張經典的矩陣表格繼續說明共享鎖和排他鎖的互斥關係:測試
-- | S | X |
---|---|---|
S | 0 | 1 |
X | 1 | 1 |
圖中S表示共享鎖X表示獨佔鎖,0表示鎖兼容1表示鎖衝突,兼容不被阻塞,衝突被阻塞。由表可知一旦一個事務加了排他鎖,其餘個事務加任何鎖都須要等待。多個共享鎖不會相互阻塞。spa
這三種類型的鎖都描述了鎖定的範圍,故放在一塊兒說明。code
如下定義摘自MySQL官方文檔
- 記錄鎖(Record Locks):記錄鎖鎖定索引中一條記錄。
- 間隙鎖(Gap Locks):間隙鎖要麼鎖住索引記錄中間的值,要麼鎖住第一個索引記錄前面的值或者最後一個索引記錄後面的值。
- Next-Key Locks:Next-Key鎖是索引記錄上的記錄鎖和在索引記錄以前的間隙鎖的組合。
定義中都提到了索引記錄(index record)。爲何?行鎖和索引有什麼關係呢?其實,InnoDB是經過搜索或者掃描表中索引來完成加鎖操做,InnoDB會爲他遇到的每個索引數據加上共享鎖或排他鎖。因此咱們能夠稱行級鎖(row-level locks)爲索引記錄鎖(index-record locks),由於行級鎖是添加到行對應的索引上的。
三種類型鎖的鎖定範圍不一樣,且逐漸擴大。咱們來舉一個例子來簡要說明各類鎖的鎖定範圍,假設表t中索引列有三、五、八、9四個數字值,根據官方文檔的肯定三種鎖的鎖定範圍以下:
最後對於間隙鎖還須要補充三點:
前面咱們已經介紹了InnoDB的是在SQL語句的執行過程當中經過掃描索引記錄的方式來實現加鎖行爲的。那哪些些語句會加鎖?加什麼樣的鎖?接下來咱們逐一描述:
最後補充兩點:
閒言少敘,接下來咱們進入本文重點SQL語句驗證部分。
數據庫:MySQL 5.6.35
事務隔離級別:Repeatable read
數據庫訪問終端:mysql client
建表:
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入數據:
INSERT INTO `user` (`id`, `name`) VALUES ('1', 'a'); INSERT INTO `user` (`id`, `name`) VALUES ('3', 'c'); INSERT INTO `user` (`id`, `name`) VALUES ('5', 'e'); INSERT INTO `user` (`id`, `name`) VALUES ('7', 'g'); INSERT INTO `user` (`id`, `name`) VALUES ('9', 'i');
首先咱們執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name='e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`) VALUES (10, #{name}); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
a | 不阻塞 |
b | 不阻塞 |
d | 阻塞 |
e | 阻塞 |
f | 阻塞 |
h | 不阻塞 |
i | 不阻塞 |
觀察結果,咱們發現SQL語句SELECT * FROM user where name='e' for update
一共鎖住索引name中三行記錄,(c,e]區間應該是next-key鎖而(e,h)區間是索引記錄e後面的間隙。
接下來咱們肯定next-key鎖中哪部分是索引記錄鎖哪部分是間隙鎖。
執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name='e' for update; | -- |
3 | -- | SELECT * FROM user where name=#{name} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
d | 不阻塞 |
e | 阻塞 |
f | 不阻塞 |
由於間隙鎖只會阻止insert語句,因此一樣的索引數據,insert
語句阻塞而select for update
語句不阻塞的就是間隙鎖,若是兩條語句都阻塞就是索引記錄鎖。
觀察執行結果可知,d和f爲間隙鎖,e爲索引記錄鎖。
結論:經過兩條SQL,咱們肯定了對於輔助索引name在查詢條件爲 where name='e'
時的加鎖範圍爲(c,e],(e,g),其中:
說的這裏細心的讀者可能已經發現咱們的測試數據中沒有間隙的邊界數據c和g。接下來咱們就對間隙邊界值進行測試。
執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name='e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`) VALUES (#{id}, #{name}); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id,name的值,觀察結果:
id的值 | name=c | 執行結果 | id的值 | name=g | 執行結果 |
---|---|---|---|---|---|
-- | -- | -- | -3 | g | 組塞 |
-- | -- | -- | -2 | g | 阻塞 |
-1 | c | 不阻塞 | -1 | g | 阻塞 |
1 | c | 不阻塞 | 1 | g | 不阻塞 |
2 | c | 不阻塞 | 2 | g | 阻塞 |
3 | c | 不阻塞 | 3 | g | 不阻塞 |
4 | c | 阻塞 | 4 | g | 阻塞 |
5 | c | 阻塞 | 5 | g | 阻塞 |
6 | c | 阻塞 | 6 | g | 阻塞 |
7 | c | 不阻塞 | 7 | g | 不阻塞 |
8 | c | 阻塞 | 8 | g | 不阻塞 |
9 | c | 不阻塞 | 9 | g | 不阻塞 |
10 | c | 阻塞 | 10 | g | 不阻塞 |
11 | c | 阻塞 | - | - | - |
12 | c | 阻塞 | - | - | - |
經過觀察以上執行結果,咱們發現,name等於c和e時insert
語句的結果隨着id值得不一樣一下子鎖定,一下子不鎖定。那必定是id列加了鎖纔會形成這樣的結果。
若是先不看id=5
這一行數據的結果,咱們發現一個規律:
name=c
時,name=c
對應的id=3
的id聚合索引數據記錄以後的間隙(3,5),(5,7),(7,9),(9,∞)都被加上了鎖。name=e
時,name=e
對應的id=7
的id聚合索引數據記錄以前的間隙(5,7),(3,5),(1,3),(-∞,1)都被加上了鎖。select * from user where id = x for update;
語句判斷出以上間隙上加的鎖都爲間隙鎖。接下來咱們解釋一下id=5
的鎖定狀況
執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name='e' for update; | -- |
3 | -- | SELECT * FROM user where id=#{id} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id的值,觀察結果:
id的值 | 執行結果 |
---|---|
3 | 不阻塞 |
4 | 不阻塞 |
5 | 阻塞 |
6 | 不阻塞 |
7 | 不阻塞 |
經過觀察執行結果可知,id=5
的聚合索引記錄上添加了索引記錄鎖。根據MySQL官方文檔描述,InnoDB引擎在對輔助索引加鎖的時候,也會對輔助索引所在行所對應的聚合索引(主鍵)加鎖。而主鍵是惟一索引,在對惟一索引加鎖時,間隙鎖失效,只使用索引記錄鎖。因此SELECT * FROM user where name='e' for update;
不只對輔助索引name=e
列加上了next-key鎖,還對對應的聚合索引id=5
列加上了索引記錄鎖。
最終結論:
對於SELECT * FROM user where name='e' for update;
一共有三種鎖定行爲:
上面咱們將對輔助索引加鎖的狀況介紹完了,接下來咱們測試一下對聚合索引和惟一索引加鎖。
建表:
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
注意與場景一表user不一樣的是name列爲惟一索引。
插入數據:
INSERT INTO `user` (`id`, `name`) VALUES ('1', 'a'); INSERT INTO `user` (`id`, `name`) VALUES ('3', 'c'); INSERT INTO `user` (`id`, `name`) VALUES ('5', 'e'); INSERT INTO `user` (`id`, `name`) VALUES ('7', 'g'); INSERT INTO `user` (`id`, `name`) VALUES ('9', 'i');
首先咱們執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name='e' for update; | |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`) VALUES (10, #{name}); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
a | 不阻塞 |
b | 不阻塞 |
c | 不阻塞 |
d | 不阻塞 |
e | 阻塞 |
f | 不阻塞 |
g | 不阻塞 |
h | 不阻塞 |
i | 不阻塞 |
由測試結果可知,只有name='e'
這行數據被鎖定。
經過SQL語句咱們驗證了,對於惟一索引列加鎖,間隙鎖失效,
場景一和場景二都是在查詢條件等於的狀況下作出的範圍判斷,如今咱們嘗試一下其餘查詢條件,看看結論是否一致。
借用場景一的表和數據。
建表:
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, PRIMARY KEY (`id`), KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入數據:
INSERT INTO `user` (`id`, `name`) VALUES ('1', 'a'); INSERT INTO `user` (`id`, `name`) VALUES ('3', 'c'); INSERT INTO `user` (`id`, `name`) VALUES ('5', 'e'); INSERT INTO `user` (`id`, `name`) VALUES ('7', 'g'); INSERT INTO `user` (`id`, `name`) VALUES ('9', 'i');
執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`) VALUES ('10', #{name}); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
a | 阻塞 |
b | 阻塞 |
c | 阻塞 |
d | 阻塞 |
e | 阻塞 |
f | 阻塞 |
g | 阻塞 |
h | 阻塞 |
i | 阻塞 |
這個結果是否是和你想象的不太同樣,這個結果代表where name>'e'
這個查詢條件並非鎖住'e'
列以後的數據,而鎖住了全部name
列中全部數據和間隙。這是爲何呢?
咱們執行如下的SQL語句執行計劃:
explain select * from user where name>'e' for update;
執行結果:
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ | 1 | SIMPLE | user | index | index_name | index_name | 26 | NULL | 5 | Using where; Using index | +----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
若是你的結果與上面不一樣先執行一下OPTIMIZE TABLE user;
再執行以上語句。
經過觀察SQL語句的執行計劃咱們發現,語句使用了name
列索引,且rows
參數等於5,user表中一共也只有5行數據。SQL語句的執行過程當中一共掃描了name
索引記錄5行數據且對這5行數據都加上了next-key鎖,符合咱們上面的執行結果。
接下來咱們再製造一組數據。
建表:
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入數據:
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('1', 'a','15'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('3', 'c','20'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('5', 'e','16'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('7', 'g','19'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('9', 'i','34');
這張表和前表的區別是多了一列非索引列age
。
咱們再執行一下一樣的SQL語句執行計劃:
explain select * from user where name>'e' for update;
執行結果:
+----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ | 1 | SIMPLE | user | range | index_name | index_name | 26 | NULL | 2 | Using index condition | +----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ 1 row in set (0.00 sec)
是否是和第一次執行結果不一樣了,rows
參數等於2,說明掃描了兩行記錄,結合SQL語句select * from user where name>'e' for update;
執行後返回結果咱們判斷這兩行記錄應該爲g和i。
由於select * from user where name>'e' for update;
語句掃描了兩行索引記錄分別是g和i,因此咱們將g和i的鎖定範圍疊就能夠獲得where name>'e'
的鎖定範圍:
name
列鎖定範圍爲(e,g],(g,i)。索引記錄i的在name
列鎖定範圍爲(g,i],(i,+∞)。二者疊加後鎖定範圍爲(e,g],(g,i],(i,+∞)。其中g,i爲索引記錄鎖。id
列中的7和9加索引記錄鎖。name
列的值爲鎖定範圍上邊界e時,還會在e所對應的id
列值爲5以後的全部值之間加上間隙鎖,範圍爲(5,7),(7,9),(9,+∞)。下邊界爲+∞無需考慮。接下來咱們逐一測試:
首先測試驗證了next-key鎖範圍,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18'); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
a | 不阻塞 |
b | 不阻塞 |
c | 不阻塞 |
d | 不阻塞 |
f | 阻塞 |
g | 阻塞 |
h | 阻塞 |
i | 阻塞 |
j | 阻塞 |
k | 阻塞 |
下面驗證next-key鎖中哪部分是間隙鎖,哪部分是索引記錄鎖,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | SELECT * FROM user where name=#{name} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
e | 不阻塞 |
f | 不阻塞 |
g | 阻塞 |
h | 不阻塞 |
i | 阻塞 |
j | 不阻塞 |
接下來驗證對id
列加索引記錄鎖,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | SELECT * FROM user where id=#{id} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id的值,觀察結果:
id的值 | 執行結果 |
---|---|
5 | 不阻塞 |
6 | 不阻塞 |
7 | 阻塞 |
8 | 不阻塞 |
9 | 阻塞 |
10 | 不阻塞 |
最後咱們驗證name
列的值爲邊界數據e時,id
列間隙鎖的範圍,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18'); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id的值,觀察結果:
id的值 | 執行結果 |
---|---|
-1 | 不阻塞 |
1 | 不阻塞 |
2 | 不阻塞 |
3 | 不阻塞 |
4 | 不阻塞 |
5 | 不阻塞 |
6 | 阻塞 |
7 | 阻塞 |
8 | 阻塞 |
9 | 阻塞 |
10 | 阻塞 |
11 | 阻塞 |
12 | 阻塞 |
注意7和9是索引記錄鎖記錄鎖。
觀察上面的全部SQL語句執行結果,能夠驗證select * from user where name>'e' for update
的鎖定範圍爲此語句掃描name
列索引記錄g和i的鎖定範圍的疊加組合。
咱們經過場景三驗證了普通索引的範圍查詢語句加鎖範圍,如今咱們來驗證一下惟一索引的範圍查詢狀況下的加鎖範圍。有了場景三的鋪墊咱們直接跳過掃描所有索引的狀況,建立能夠掃描範圍記錄的表結構並插入相應數據測試。
建表:
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(8) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `index_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入數據:
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('1', 'a','15'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('3', 'c','20'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('5', 'e','16'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('7', 'g','19'); INSERT INTO `user` (`id`, `name`,`age`) VALUES ('9', 'i','34');
和場景三表惟一不一樣是name
列爲惟一索引。
SQL語句select * from user where name>'e'
掃描name
列兩條索引記錄g和i。若是須要只對g和i這兩條記錄加上記錄鎖沒法避免幻讀的發生,索引鎖定範圍應該仍是兩條數據next-key鎖鎖的組合:(e,g],(g,i],(i,+∞)。其中g,i爲索引記錄鎖。
咱們經過SQL驗證咱們的結論,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18'); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
a | 不阻塞 |
b | 不阻塞 |
c | 不阻塞 |
d | 不阻塞 |
f | 阻塞 |
g | 阻塞 |
h | 阻塞 |
i | 阻塞 |
j | 阻塞 |
k | 阻塞 |
下面驗證next-key鎖中哪部分是間隙鎖,哪部分是索引記錄鎖,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | SELECT * FROM user where name=#{name} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中name的值,觀察結果:
name的值 | 執行結果 |
---|---|
e | 不阻塞 |
f | 不阻塞 |
g | 阻塞 |
h | 不阻塞 |
i | 阻塞 |
j | 不阻塞 |
經過上面兩條SQL語句的驗證結果,咱們證實了咱們的g和i的鎖定範圍趨勢爲二者next-key疊加組合。
接下來咱們驗證一下對輔助索引加鎖後對聚合索引的鎖轉移,執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | SELECT * FROM user where id=#{id} for update; |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id的值,觀察結果:
id的值 | 執行結果 |
---|---|
5 | 不阻塞 |
6 | 不阻塞 |
7 | 阻塞 |
8 | 不阻塞 |
9 | 阻塞 |
10 | 不阻塞 |
由結果可知對輔助索引name
中的g和i列對應的聚合索引id
列中的7和9加上了索引記錄鎖。
到目前爲止全部實驗結果和場景三徹底同樣,這也很好理解,畢竟場景四和場景三隻是輔助索引name
的索引類型不一樣,一個是惟一索引,一個是普通索引。
最後驗證意向,next-key鎖邊界數據e,看看結論時候和場景三相同。
執行SQL語句的模板:
步驟 | client 1 | client 2 |
---|---|---|
1 | begin; | -- |
2 | SELECT * FROM user where name>'e' for update; | -- |
3 | -- | begin; |
4 | -- | INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18'); |
5 | rollback; | -- |
6 | -- | rollback; |
替換步驟5中id的值,觀察結果:
id的值 | 執行結果 |
---|---|
-1 | 不阻塞 |
1 | 不阻塞 |
2 | 不阻塞 |
3 | 不阻塞 |
4 | 不阻塞 |
5 | 不阻塞 |
6 | 不阻塞 |
7 | 阻塞 |
8 | 不阻塞 |
9 | 阻塞 |
10 | 不阻塞 |
11 | 不阻塞 |
12 | 不阻塞 |
注意7和9是索引記錄鎖記錄鎖。
經過結果可知,當name
列爲索引記錄上邊界e時,並無對id有加鎖行爲,這點與場景三不一樣。
對於惟一索引的範圍查詢和普通索引的範圍查詢相似,惟一不一樣的是當輔助索引等於上下範圍的邊界值是不會對主鍵加上間隙鎖。
惟一索引範圍查詢加鎖範圍:
InnoDB引擎會對他掃描過的索引記錄加上相應的鎖,經過「場景一」咱們已經明確了掃描一條普通索引記錄的鎖定範圍,經過「場景三」咱們能夠推斷任意多個掃描普通索引索引記錄的鎖定範圍。經過「場景二」咱們肯定了掃描一條惟一索引記錄(或主鍵)的鎖定範圍。經過「場景四」咱們能夠推斷任意多個掃描索惟一引記錄(或主鍵)的鎖定範圍。在實際的應用能夠靈活使用,判斷兩條SQL語句是否相互鎖定。這裏還須要注意的是對於索引的查詢條件,不能想固然的理解,他每每不是咱們理解的樣子,須要結合執行計劃判斷索引最終掃描的記錄數,不然會對加鎖範圍理解產生誤差。
注1:在事務隔離級別爲SERIALIZABLE時,普通的select語句也會對語句執行過程當中掃描過的索引加上next-key鎖。若是語句掃描的是惟一索引,那就將next-key鎖降級爲索引記錄鎖了。
注2:當更新語句修改聚合索引(主鍵)記錄時,會對受影響的輔助索引執行隱性的加鎖操做。當插入新的輔助索引記錄以前執行重複檢查掃描時和當插入新的輔助索引記錄時,更新操做還對受影響的輔助索引記錄添加共享鎖。
https://dev.mysql.com/doc/ref...
https://dev.mysql.com/doc/ref...