真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

MySQL數(shù)據(jù)庫InnoDB引擎行級鎖鎖定范圍是什么

這篇文章主要介紹MySQL數(shù)據(jù)庫InnoDB引擎行級鎖鎖定范圍是什么,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、成都做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢想脫穎而出為使命,1280元巴宜做網(wǎng)站,已為上家服務(wù),為巴宜各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18980820575

                                                       Mysql數(shù)據(jù)庫InnoDB引擎支持行級鎖,也就是說我們可以對表中某些行數(shù)據(jù)執(zhí)行鎖定操作,鎖定操作的影響是:如果一個(gè)事物對表中某行執(zhí)行了鎖定操作,而另一個(gè)事務(wù)也需要對同樣的行執(zhí)行鎖定操作,這樣第二個(gè)事務(wù)的鎖定操作有可能被阻塞,一旦被阻塞第二個(gè)事務(wù)只能等到第一個(gè)事務(wù)執(zhí)行完畢(提交或回滾)或超時(shí)。

背景知識(shí)

上面我們簡單的介紹了InnoDB的行級鎖,為了理解后面的驗(yàn)證部分,需要補(bǔ)充一下背景知識(shí)。如果對相應(yīng)知識(shí)非常了解,可以直接跳轉(zhuǎn)到驗(yàn)證部分內(nèi)容。

1. InnoDB鎖的類型

InnoDB引擎使用了七種類型的鎖,他們分別是:

  • 共享排他鎖(Shared and Exclusive Locks)

  • 意向鎖(Intention Locks)

  • 記錄鎖(Record Locks)

  • 間隙鎖(Gap Locks)

  • Next-Key Locks

  • 插入意圖鎖(Insert Intention Locks)

  • 自增鎖(AUTO-INC Locks)

本文主要涉及Shared and Exclusive Locks,Record Locks,Gap Locks,Next-Key Locks這幾種鎖,其他類型鎖如果大家感興趣可以自己深入了解,在此不在詳述。

1.1 Shared and Exclusive Locks

共享鎖(S鎖)和排他鎖(X鎖)的概念在許多編程語言中都出現(xiàn)過。先來描述一下這兩種鎖在MySQL中的影響結(jié)果:

  • 如果一個(gè)事務(wù)對某一行數(shù)據(jù)加了S鎖,另一個(gè)事務(wù)還可以對相應(yīng)的行加S鎖,但是不能對相應(yīng)的行加X鎖。

  • 如果一個(gè)事務(wù)對某一行數(shù)據(jù)加了X鎖,另一個(gè)事務(wù)既不能對相應(yīng)的行加S鎖也不能加X鎖。

用一張經(jīng)典的矩陣表格繼續(xù)說明共享鎖和排他鎖的互斥關(guān)系:

--SX
S01
X11

圖中S表示共享鎖X表示獨(dú)占鎖,0表示鎖兼容1表示鎖沖突,兼容不被阻塞,沖突被阻塞。由表可知一旦一個(gè)事務(wù)加了排他鎖,其他個(gè)事務(wù)加任何鎖都需要等待。多個(gè)共享鎖不會(huì)相互阻塞。

1.2 Record Locks、Gap Locks、Next-Key Locks

這三種類型的鎖都描述了鎖定的范圍,故放在一起說明。

以下定義摘自MySQL官方文檔

  • 記錄鎖(Record Locks):記錄鎖鎖定索引中一條記錄。

  • 間隙鎖(Gap Locks):間隙鎖要么鎖住索引記錄中間的值,要么鎖住第一個(gè)索引記錄前面的值或者最后一個(gè)索引記錄后面的值。

  • Next-Key Locks:Next-Key鎖是索引記錄上的記錄鎖和在索引記錄之前的間隙鎖的組合。

定義中都提到了索引記錄(index record)。為什么?行鎖和索引有什么關(guān)系呢?其實(shí),InnoDB是通過搜索或者掃描表中索引來完成加鎖操作,InnoDB會(huì)為他遇到的每一個(gè)索引數(shù)據(jù)加上共享鎖或排他鎖。所以我們可以稱行級鎖(row-level locks)為索引記錄鎖(index-record locks),因?yàn)樾屑夋i是添加到行對應(yīng)的索引上的。

三種類型鎖的鎖定范圍不同,且逐漸擴(kuò)大。我們來舉一個(gè)例子來簡要說明各種鎖的鎖定范圍,假設(shè)表t中索引列有3、5、8、9四個(gè)數(shù)字值,根據(jù)官方文檔的確定三種鎖的鎖定范圍如下:

  • 記錄鎖的鎖定范圍是單獨(dú)的索引記錄,就是3、5、8、9這四行數(shù)據(jù)。

  • 間隙鎖的鎖定為行中間隙,用集合表示為(-∞,3)、(3,5)、(5,8)、(8,9)、(9,+∞)。

  • Next-Key鎖是有索引記錄鎖加上索引記錄鎖之前的間隙鎖組合而成,用集合的方式表示為(-∞,3]、(3,5]、(5,8]、(8,9]、(9,+∞)。

最后對于間隙鎖還需要補(bǔ)充三點(diǎn):

  1. 間隙鎖阻止其他事務(wù)對間隙數(shù)據(jù)的并發(fā)插入,這樣可有有效的解決幻讀問題(Phantom Problem)。正因?yàn)槿绱耍?strong>并不是所有事務(wù)隔離級別都使用間隙鎖,MySQL InnoDB引擎只有在Repeatable Read(默認(rèn))隔離級別才使用間隙鎖。

  2. 間隙鎖的作用只是用來阻止其他事務(wù)在間隙中插入數(shù)據(jù),他不會(huì)阻止其他事務(wù)擁有同樣的的間隙鎖。這就意味著,除了insert語句,允許其他SQL語句可以對同樣的行加間隙鎖而不會(huì)被阻塞。

  3. 對于唯一索引的加鎖行為,間隙鎖就會(huì)失效,此時(shí)只有記錄鎖起作用。

2. 加鎖語句

前面我們已經(jīng)介紹了InnoDB的是在SQL語句的執(zhí)行過程中通過掃描索引記錄的方式來實(shí)現(xiàn)加鎖行為的。那哪些些語句會(huì)加鎖?加什么樣的鎖?接下來我們逐一描述:

  • select ... from語句:InnoDB引擎采用多版本并發(fā)控制(MVCC)的方式實(shí)現(xiàn)了非阻塞讀,所以對于普通的select讀語句,InnoDB并不會(huì)加鎖【注1】。

  • select ... from lock in share mode語句:這條語句和普通select語句的區(qū)別就是后面加了lock in share mode,通過字面意思我們可以猜到這是一條加鎖的讀語句,并且鎖類型為共享鎖(讀鎖)。InnoDB會(huì)對搜索的所有索引記錄加next-key鎖,但是如果掃描的唯一索引的唯一行,next-key降級為索引記錄鎖。

  • select ... from for update語句:和上面的語句一樣,這條語句加的是排他鎖(寫鎖)。InnoDB會(huì)對搜索的所有索引記錄加next-key鎖,但是如果掃描唯一索引的唯一行,next-key降級為索引記錄鎖。

  • update ... where ...語句:。InnoDB會(huì)對搜索的所有索引記錄加next-key鎖,但是如果掃描唯一索引的唯一行,next-key降級為索引記錄鎖?!咀?】

  • delete ... where ...語句:。InnoDB會(huì)對搜索的所有索引記錄加next-key鎖,但是如果掃描唯一索引的唯一行,next-key降級為索引記錄鎖。

  • insert語句:InnoDB只會(huì)在將要插入的那一行上設(shè)置一個(gè)排他的索引記錄鎖。

最后補(bǔ)充兩點(diǎn):

  1. 如果一個(gè)查詢使用了輔助索引并且在索引記錄加上了排他鎖,InnoDB會(huì)在相對應(yīng)的聚合索引記錄上加鎖。

  2. 如果你的SQL語句無法使用索引,這樣MySQL必須掃描整個(gè)表以處理該語句,導(dǎo)致的結(jié)果就是表的每一行都會(huì)被鎖定,并且阻止其他用戶對該表的所有插入。

SQL語句驗(yàn)證

閑言少敘,接下來我們進(jìn)入本文重點(diǎn)SQL語句驗(yàn)證部分。

1.測試環(huán)境

數(shù)據(jù)庫:MySQL 5.6.35  
事務(wù)隔離級別:Repeatable read  
數(shù)據(jù)庫訪問終端:mysql client

2.驗(yàn)證場景

2.1 場景一

建表:

CREATE TABLE `user` (
 `id` int(11) NOT NULL,
 `name` varchar(8) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入數(shù)據(jù):

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');

首先我們執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name='e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`) VALUES (10, #{name});
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
a不阻塞
b不阻塞
d阻塞
e阻塞
f阻塞
h不阻塞
i不阻塞

觀察結(jié)果,我們發(fā)現(xiàn)SQL語句
SELECT * FROM user where name='e' for update
一共鎖住索引name中三行記錄,(c,e]區(qū)間應(yīng)該是next-key鎖而(e,h)區(qū)間是索引記錄e后面的間隙。

接下來我們確定next-key鎖中哪部分是索引記錄鎖哪部分是間隙鎖。

執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name='e' for update;--
3--SELECT * FROM user where name=#{name} for update;
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
d不阻塞
e阻塞
f不阻塞

因?yàn)殚g隙鎖只會(huì)阻止insert語句,所以同樣的索引數(shù)據(jù),insert語句阻塞而select for update語句不阻塞的就是間隙鎖,如果兩條語句都阻塞就是索引記錄鎖。

觀察執(zhí)行結(jié)果可知,d和f為間隙鎖,e為索引記錄鎖。

結(jié)論:通過兩條SQL,我們確定了對于輔助索引name在查詢條件為 where name='e'  時(shí)的加鎖范圍為(c,e],(e,g),其中:

  • 對SQL語句掃描的索引記錄e加索引記錄鎖[e]。

  • 鎖定了e前面的間隙,c到e之間的數(shù)據(jù)(c,e)加了間隙鎖

  • 前兩個(gè)構(gòu)成了next-key鎖(c,e]。

  • 值得注意的是還鎖定了e后面的間隙(e,g)。

說的這里細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn)我們的測試數(shù)據(jù)中沒有間隙的邊界數(shù)據(jù)c和g。接下來我們就對間隙邊界值進(jìn)行測試。

執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name='e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`) VALUES (#{id}, #{name});
5rollback;--
6--rollback;

替換步驟5中id,name的值,觀察結(jié)果:

id的值name=c執(zhí)行結(jié)果id的值name=g執(zhí)行結(jié)果
-------3g組塞
-------2g阻塞
-1c不阻塞-1g阻塞
1c不阻塞1g不阻塞
2c不阻塞2g阻塞
3c不阻塞3g不阻塞
4c阻塞4g阻塞
5c阻塞5g阻塞
6c阻塞6g阻塞
7c不阻塞7g不阻塞
8c阻塞8g不阻塞
9c不阻塞9g不阻塞
10c阻塞10g不阻塞
11c阻塞---
12c阻塞---

通過觀察以上執(zhí)行結(jié)果,我們發(fā)現(xiàn),name等于c和e時(shí)insert語句的結(jié)果隨著id值得不同一會(huì)兒鎖定,一會(huì)兒不鎖定。那一定是id列加了鎖才會(huì)造成這樣的結(jié)果。

如果先不看id=5這一行數(shù)據(jù)的結(jié)果,我們發(fā)現(xiàn)一個(gè)規(guī)律:

  • 當(dāng)name=c時(shí),name=c對應(yīng)的id=3的id聚合索引數(shù)據(jù)記錄之后的間隙(3,5),(5,7),(7,9),(9,∞)都被加上了鎖。

  • 當(dāng)name=e時(shí),name=e對應(yīng)的id=7的id聚合索引數(shù)據(jù)記錄之前的間隙(5,7),(3,5),(1,3),(-∞,1)都被加上了鎖。

  • 我們可用select * from user where id = x for update;語句判斷出以上間隙上加的鎖都為間隙鎖。

接下來我們解釋一下id=5的鎖定情況

執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name='e' for update;--
3--SELECT * FROM user where id=#{id} for update;
5rollback;--
6--rollback;

替換步驟5中id的值,觀察結(jié)果:

id的值執(zhí)行結(jié)果
3不阻塞
4不阻塞
5阻塞
6不阻塞
7 不阻塞

通過觀察執(zhí)行結(jié)果可知,id=5的聚合索引記錄上添加了索引記錄鎖。根據(jù)MySQL官方文檔描述,InnoDB引擎在對輔助索引加鎖的時(shí)候,也會(huì)對輔助索引所在行所對應(yīng)的聚合索引(主鍵)加鎖。而主鍵是唯一索引,在對唯一索引加鎖時(shí),間隙鎖失效,只使用索引記錄鎖。所以SELECT * FROM user where name='e' for update;不僅對輔助索引name=e列加上了next-key鎖,還對對應(yīng)的聚合索引id=5列加上了索引記錄鎖。

最終結(jié)論: 
對于SELECT * FROM user where name='e' for update;一共有三種鎖定行為:

  1. 對SQL語句掃描過的輔助索引記錄行加上next-key鎖(注意也鎖住記錄行之后的間隙)。

  2. 對輔助索引對應(yīng)的聚合索引加上索引記錄鎖。

  3. 當(dāng)輔助索引為間隙鎖“最小”和“最大”值時(shí),對聚合索引相應(yīng)的行加間隙鎖?!白钚 辨i定對應(yīng)聚合索引之后的行間隙?!白畲蟆敝垫i定對應(yīng)聚合索引之前的行間隙。

上面我們將對輔助索引加鎖的情況介紹完了,接下來我們測試一下對聚合索引和唯一索引加鎖。

2.2 場景二

建表:

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列為唯一索引。

插入數(shù)據(jù):

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');

首先我們執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name='e' for update;
3--begin;
4--INSERT INTO `user` (`id`, `name`) VALUES (10, #{name});
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
a不阻塞
b不阻塞
c不阻塞
d不阻塞
e阻塞
f不阻塞
g不阻塞
h不阻塞
i不阻塞

由測試結(jié)果可知,只有name='e'這行數(shù)據(jù)被鎖定。

通過SQL語句我們驗(yàn)證了,對于唯一索引列加鎖,間隙鎖失效,

2.3 場景三

場景一和場景二都是在查詢條件等于的情況下做出的范圍判斷,現(xiàn)在我們嘗試一下其他查詢條件,看看結(jié)論是否一致。

借用場景一的表和數(shù)據(jù)。

建表:

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;

插入數(shù)據(jù):

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');

執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`) VALUES ('10', #{name});
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
a阻塞
b阻塞
c阻塞
d阻塞
e阻塞
f阻塞
g阻塞
h阻塞
i阻塞

這個(gè)結(jié)果是不是和你想象的不太一樣,這個(gè)結(jié)果表明where name>'e'這個(gè)查詢條件并不是鎖住'e'列之后的數(shù)據(jù),而鎖住了所有name列中所有數(shù)據(jù)和間隙。這是為什么呢?

我們執(zhí)行以下的SQL語句執(zhí)行計(jì)劃:

 explain select * from user where name>'e' for update;

執(zhí)行結(jié)果:

+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| 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)

如果你的結(jié)果與上面不同先執(zhí)行一下OPTIMIZE TABLE user;再執(zhí)行以上語句。

通過觀察SQL語句的執(zhí)行計(jì)劃我們發(fā)現(xiàn),語句使用了name列索引,且rows參數(shù)等于5,user表中一共也只有5行數(shù)據(jù)。SQL語句的執(zhí)行過程中一共掃描了name索引記錄5行數(shù)據(jù)且對這5行數(shù)據(jù)都加上了next-key鎖,符合我們上面的執(zhí)行結(jié)果。

接下來我們再制造一組數(shù)據(jù)。  
建表:

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;

插入數(shù)據(jù):

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');

這張表和前表的區(qū)別是多了一列非索引列age

我們再執(zhí)行一下同樣的SQL語句執(zhí)行計(jì)劃:

 explain select * from user where name>'e' for update;

執(zhí)行結(jié)果:

+----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+
| 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)

是不是和第一次執(zhí)行結(jié)果不同了,rows參數(shù)等于2,說明掃描了兩行記錄,結(jié)合SQL語句select * from user where name>'e' for update;執(zhí)行后返回結(jié)果我們判斷這兩行記錄應(yīng)該為g和i。

因?yàn)?code>select * from user where name>'e' for update;語句掃描了兩行索引記錄分別是g和i,所以我們將g和i的鎖定范圍疊就可以得到where name>'e'的鎖定范圍:

  1. 索引記錄g在name列鎖定范圍為(e,g],(g,i)。索引記錄i的在name列鎖定范圍為(g,i],(i,+∞)。兩者疊加后鎖定范圍為(e,g],(g,i],(i,+∞)。其中g(shù),i為索引記錄鎖。

  2. g和i對應(yīng)id列中的7和9加索引記錄鎖。

  3. 當(dāng)name列的值為鎖定范圍上邊界e時(shí),還會(huì)在e所對應(yīng)的id列值為5之后的所有值之間加上間隙鎖,范圍為(5,7),(7,9),(9,+∞)。下邊界為+∞無需考慮。

接下來我們逐一測試:

首先測試驗(yàn)證了next-key鎖范圍,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18');
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
a不阻塞
b不阻塞
c不阻塞
d不阻塞
f阻塞
g阻塞
h阻塞
i阻塞
j阻塞
k阻塞

下面驗(yàn)證next-key鎖中哪部分是間隙鎖,哪部分是索引記錄鎖,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where name=#{name} for update;
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
e不阻塞
f不阻塞
g阻塞
h不阻塞
i阻塞
j不阻塞

接下來驗(yàn)證對id列加索引記錄鎖,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where id=#{id} for update;
5rollback;--
6--rollback;

替換步驟5中id的值,觀察結(jié)果:

id的值執(zhí)行結(jié)果
5不阻塞
6 不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞

最后我們驗(yàn)證name列的值為邊界數(shù)據(jù)e時(shí),id列間隙鎖的范圍,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18');
5rollback;--
6--rollback;

替換步驟5中id的值,觀察結(jié)果:

id的值執(zhí)行結(jié)果
-1不阻塞
1不阻塞
2不阻塞
3不阻塞
4不阻塞
5不阻塞
6阻塞
7阻塞
8阻塞
9阻塞
10阻塞
11阻塞
12阻塞

注意7和9是索引記錄鎖記錄鎖

觀察上面的所有SQL語句執(zhí)行結(jié)果,可以驗(yàn)證select * from user where name>'e' for update的鎖定范圍為此語句掃描name列索引記錄g和i的鎖定范圍的疊加組合。

2.4 場景四

我們通過場景三驗(yàn)證了普通索引的范圍查詢語句加鎖范圍,現(xiàn)在我們來驗(yàn)證一下唯一索引的范圍查詢情況下的加鎖范圍。有了場景三的鋪墊我們直接跳過掃描全部索引的情況,創(chuàng)建可以掃描范圍記錄的表結(jié)構(gòu)并插入相應(yīng)數(shù)據(jù)測試。

建表:

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;

插入數(shù)據(jù):

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這兩條記錄加上記錄鎖無法避免幻讀的發(fā)生,索引鎖定范圍應(yīng)該還是兩條數(shù)據(jù)next-key鎖鎖的組合:(e,g],(g,i],(i,+∞)。其中g(shù),i為索引記錄鎖

我們通過SQL驗(yàn)證我們的結(jié)論,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18');
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
a不阻塞
b不阻塞
c不阻塞
d不阻塞
f阻塞
g阻塞
h阻塞
i阻塞
j阻塞
k阻塞

下面驗(yàn)證next-key鎖中哪部分是間隙鎖,哪部分是索引記錄鎖,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where name=#{name} for update;
5rollback;--
6--rollback;

替換步驟5中name的值,觀察結(jié)果:

name的值執(zhí)行結(jié)果
e不阻塞
f不阻塞
g阻塞
h不阻塞
i阻塞
j不阻塞

通過上面兩條SQL語句的驗(yàn)證結(jié)果,我們證明了我們的g和i的鎖定范圍趨勢為兩者next-key疊加組合。

接下來我們驗(yàn)證一下對輔助索引加鎖后對聚合索引的鎖轉(zhuǎn)移,執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where id=#{id} for update;
5rollback;--
6--rollback;

替換步驟5中id的值,觀察結(jié)果:

id的值執(zhí)行結(jié)果
5不阻塞
6 不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞

由結(jié)果可知對輔助索引name中的g和i列對應(yīng)的聚合索引id列中的7和9加上了索引記錄鎖。

到目前為止所有實(shí)驗(yàn)結(jié)果和場景三完全一樣,這也很好理解,畢竟場景四和場景三只是輔助索引name的索引類型不同,一個(gè)是唯一索引,一個(gè)是普通索引。

最后驗(yàn)證意向,next-key鎖邊界數(shù)據(jù)e,看看結(jié)論時(shí)候和場景三相同。

執(zhí)行SQL語句的模板:

步驟client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18');
5rollback;--
6--rollback;

替換步驟5中id的值,觀察結(jié)果:

id的值執(zhí)行結(jié)果
-1不阻塞
1不阻塞
2不阻塞
3不阻塞
4不阻塞
5不阻塞
6不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞
11不阻塞
12不阻塞

注意7和9是索引記錄鎖記錄鎖

通過結(jié)果可知,當(dāng)name列為索引記錄上邊界e時(shí),并沒有對id有加鎖行為,這點(diǎn)與場景三不同。

對于唯一索引的范圍查詢和普通索引的范圍查詢類似,唯一不同的是當(dāng)輔助索引等于上下范圍的邊界值是不會(huì)對主鍵加上間隙鎖。

唯一索引范圍查詢加鎖范圍:

  • 對于掃描的輔助索引記錄的鎖定范圍就是多個(gè)索引記錄next-key范圍的疊加組合。

  • 對于聚合索引(主鍵)的鎖定范圍,會(huì)對多個(gè)輔助索引對應(yīng)的聚合索引列加索引記錄鎖。

結(jié)論

InnoDB引擎會(huì)對他掃描過的索引記錄加上相應(yīng)的鎖,通過“場景一”我們已經(jīng)明確了掃描一條普通索引記錄的鎖定范圍,通過“場景三”我們可以推斷任意多個(gè)掃描普通索引索引記錄的鎖定范圍。通過“場景二”我們確定了掃描一條唯一索引記錄(或主鍵)的鎖定范圍。通過“場景四”我們可以推斷任意多個(gè)掃描索唯一引記錄(或主鍵)的鎖定范圍。在實(shí)際的應(yīng)用可以靈活使用,判斷兩條SQL語句是否相互鎖定。這里還需要注意的是對于索引的查詢條件,不能想當(dāng)然的理解,他往往不是我們理解的樣子,需要結(jié)合執(zhí)行計(jì)劃判斷索引最終掃描的記錄數(shù),否則會(huì)對加鎖范圍理解產(chǎn)生偏差。




備注

注1:在事務(wù)隔離級別為SERIALIZABLE時(shí),普通的select語句也會(huì)對語句執(zhí)行過程中掃描過的索引加上next-key鎖。如果語句掃描的是唯一索引,那就將next-key鎖降級為索引記錄鎖了。  
注2:當(dāng)更新語句修改聚合索引(主鍵)記錄時(shí),會(huì)對受影響的輔助索引執(zhí)行隱性的加鎖操作。當(dāng)插入新的輔助索引記錄之前執(zhí)行重復(fù)檢查掃描時(shí)和當(dāng)插入新的輔助索引記錄時(shí),更新操作還對受影響的輔助索引記錄添加共享鎖。

以上是“MySQL數(shù)據(jù)庫InnoDB引擎行級鎖鎖定范圍是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


名稱欄目:MySQL數(shù)據(jù)庫InnoDB引擎行級鎖鎖定范圍是什么
網(wǎng)址分享:http://weahome.cn/article/jcsojh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部