溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL知識點之InnoDB中的行級鎖是什么

發(fā)布時間:2022-05-13 09:32:39 來源:億速云 閱讀:125 作者:zzz 欄目:MySQL數(shù)據(jù)庫

今天小編給大家分享一下MySQL知識點之InnoDB中的行級鎖是什么的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

MySQL知識點之InnoDB中的行級鎖是什么

行鎖,也稱為記錄鎖,顧名思義就是在記錄上加的鎖。但是要注意,這個記錄指的是通過給索引上的索引項加鎖。InnoDB 這種行鎖實現(xiàn)特點意味著:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖,否則,InnoDB將使用表鎖。

不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對數(shù)據(jù)加鎖。

只有執(zhí)行計劃真正使用了索引,才能使用行鎖:即便在條件中使用了索引字段,但是否使用索引來檢索數(shù)據(jù)是由MySQL 通過判斷不同執(zhí)行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。

同時當我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請求鎖時,InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖。

不過即使是行鎖,InnoDB里也是分成了各種類型的。換句話說即使對同一條記錄加行鎖,如果類型不同,起到的功效也是不同的。

這里我們還是使用前面的teacher表,增加一個索引,并插入幾條記錄。

mysql> desc teacher;
+--------+--------------+------+-----+---------+-------+
| Field  | Type         | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| number | int(11)      | NO   | PRI | NULL    |       |
| name   | varchar(100) | YES  | MUL | NULL    |       |
| domain | varchar(100) | YES  |     | NULL    |       |
+--------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> select * from teacher;
+--------+------+--------+
| number | name | domain |
+--------+------+--------+
|      1 | T    | Java   |
|      3 | M    | Redis  |
|      9 | X    | MQ     |
|     15 | O    | Python |
|     21 | A    | Golang |
+--------+------+--------+
5 rows in set (0.00 sec)

我們來看看都有哪些常用的行鎖類型。

Record Locks

也叫記錄鎖,就是僅僅把一條記錄鎖上,官方的類型名稱為:LOCK_REC_NOT_GAP。比方說我們把number值為9的那條記錄加一個記錄鎖的示意圖如下:

MySQL知識點之InnoDB中的行級鎖是什么

記錄鎖是有S鎖和X鎖之分的,當一個事務獲取了一條記錄的S型記錄鎖后,其他事務也可以繼續(xù)獲取該記錄的S型記錄鎖,但不可以繼續(xù)獲取X型記錄鎖;當一個事務獲取了一條記錄的X型記錄鎖后,其他事務既不可以繼續(xù)獲取該記錄的S型記錄鎖,也不可以繼續(xù)獲取X型記錄鎖。

T1T2
begin;
select * from teacher where number=9 for update;

select * from teacher where number=9 for update; # 阻塞

Gap Locks

MySQL在REPEATABLE READ隔離級別下是可以部分解決幻讀問題的,解決方案有兩種,可以使用MVCC方案解決,也可以采用加鎖方案解決。但是在使用加鎖方案解決時有問題,就是事務在第一次執(zhí)行讀取操作時,那些幻影記錄尚不存在,我們無法給這些幻影記錄加上記錄鎖。InnoDB提出了一種稱之為Gap Locks的鎖,官方的類型名稱為:LOCK_GAP,我們也可以簡稱為gap鎖。

間隙鎖實質(zhì)上是對索引前后的間隙上鎖,不對索引本身上鎖。

T1T2
begin;
update teacher set domain=‘Redis’ where name=‘M’;

insert into teacher value(23,‘B’,‘docker’); # 阻塞

insert into teacher value(23,‘B’,‘docker’); # 阻塞

事務T1會對([A, 21], [M, 3])、([M, 3], [O, 15])之間進行上gap鎖,如下圖中所示:

MySQL知識點之InnoDB中的行級鎖是什么

意味著不允許別的事務在這條記錄前后間隙插入新記錄,所以T2就不能插入。

但是當SQL語句變?yōu)椋?/p>

insert into teacher value(70,'P','docker');

能插入嗎?當然能,因為(70,‘P’)這條記錄不在被鎖的區(qū)間內(nèi)。

思考題

現(xiàn)在有表,表中有記錄如下:

list = ['su liang','hacker','ice']
list.insert(1,'kiko')
print(list)
#結(jié)果:['su liang', 'kiko', 'hacker', 'ice']

開啟一個事務:

begin;SELECT * FROM test1 WHERE number = 3 FOR UPDATE;

開啟另外一個事務:

INSERT INTO test1 (id, number) VALUES (2, 1); # 阻塞
INSERT INTO test1 (id, number) VALUES (3, 2); # 阻塞
INSERT INTO test1 (id, number) VALUES (6, 8); # 阻塞
INSERT INTO test1 (id, number) VALUES (8, 8); # 正常執(zhí)行
INSERT INTO test1 (id, number) VALUES (9, 9); # 正常執(zhí)行
INSERT INTO test1 (id, number) VALUES (10, 12); # 正常執(zhí)行
UPDATE test1 SET number = 5 WHERE id = 11 AND number = 12; # 阻塞

為什么(6,8)不能執(zhí)行,(8,8)可以執(zhí)行?這個間隙鎖的范圍應該是[1,8],最后一個語句為什么不能執(zhí)行?
解決思路:畫一個number的索引數(shù)據(jù)存放的圖,然后根據(jù)間隙鎖的加鎖方式,把鎖加上,就能很快明白答案。

MySQL知識點之InnoDB中的行級鎖是什么

  1. 當插入的number在(1,8)區(qū)間都會被阻塞

  2. 當插入的number等于1、8,那么id在(1,4]、[6,7)區(qū)間會被阻塞

Next-Key Locks

有時候我們既想鎖住某條記錄,又想阻止其他事務在該記錄前邊的間隙插入新記錄,所以InnoDB就提出了一種稱之為Next-Key Locks的鎖,官方的類型名稱為:LOCK_ORDINARY,我們也可以簡稱為next-key鎖。next-key鎖的本質(zhì)就是
一個記錄鎖和一個gap鎖的合體。

默認情況下,InnoDB以REPEATABLE READ隔離級別運行。在這種情況下,InnoDB使用Next-Key Locks鎖進行搜索和索引掃描,這可以防止幻讀的發(fā)生。

Insert Intention Locks

我們說一個事務在插入一條記錄時需要判斷一下插入位置是不是被別的事務加了所謂的gap鎖(next-key鎖也包含gap 鎖,后邊就不強調(diào)了),如果有的話,插入操作需要等待,直到擁有gap鎖的那個事務提交。

但是InnoDB規(guī)定事務在等待的時候也需要在內(nèi)存中生成一個鎖結(jié)構(gòu),表明有事務想在某個間隙中插入新記錄,但是現(xiàn)在處于等待狀態(tài)。這種類型的鎖命名為Insert Intention Locks,官方的類型名稱為:LOCK_INSERT_INTENTION,我們也可以稱為插入意向鎖。

可以理解為插入意向鎖是一種鎖的的等待隊列,讓等鎖的事務在內(nèi)存中進行排隊等待,當持有鎖的事務完成后,處于等待狀態(tài)的事務就可以獲得鎖繼續(xù)事務了。

隱式鎖

鎖的的維護是需要成本的,為了節(jié)約資源,MySQL在設計提出了了一個隱式鎖的概念。一般情況下INSERT操作是不加鎖的,當然真的有并發(fā)沖突的情況下下,還是會導致問題的。

所以MySQL中,一個事務對新插入的記錄可以不顯式的加鎖,但是別的事務在對這條記錄加S鎖或者X鎖時,會去檢查索引記錄中的trx_id隱藏列,然后進行各種判斷,會先幫助當前事務生成一個鎖結(jié)構(gòu),然后自己再生成一個鎖結(jié)構(gòu)后進入等待狀態(tài)。但是由于事務id的存在,相當于加了一個隱式鎖。

這樣的話,隱式鎖就起到了延遲生成鎖的用處。這個過程,我們無法干預,是由引擎自動處理的,對我們是完全透明的,我們知道下就行了。

鎖的內(nèi)存結(jié)構(gòu)

所謂的鎖其實是一個內(nèi)存中的結(jié)構(gòu),在事務執(zhí)行前本來是沒有鎖的,也就是說一開始是沒有鎖結(jié)構(gòu)和記錄進行關聯(lián)的,當一個事務想對這條記錄做改動時,首先會看看內(nèi)存中有沒有與這條記錄關聯(lián)的鎖結(jié)構(gòu),當沒有的時候就會在內(nèi)存中生成一個鎖結(jié)構(gòu)與之關聯(lián)。比方說事務T1要對記錄做改動,就需要生成一個鎖結(jié)構(gòu)與之關聯(lián)。

鎖結(jié)構(gòu)里至少要有兩個比較重要的屬性:

  • trx 信息:代表這個鎖結(jié)構(gòu)是哪個事務生成的。

  • is_waiting:代表當前事務是否在等待。

MySQL知識點之InnoDB中的行級鎖是什么

當事務T1改動了條記錄后,就生成了一個鎖結(jié)構(gòu)與該記錄關聯(lián),因為之前沒有別的事務為這條記錄加鎖,所以is_waiting 屬性就是false,我們把這個場景就稱之為獲取鎖成功,或者加鎖成功,然后就可以繼續(xù)執(zhí)行操作了。

在事務T1提交之前,另一個事務T2也想對該記錄做改動,那么先去看看有沒有鎖結(jié)構(gòu)與這條記錄關聯(lián),發(fā)現(xiàn)有一個鎖結(jié)構(gòu)與之關聯(lián)后,然后也生成了一個鎖結(jié)構(gòu)與這條記錄關聯(lián),不過鎖結(jié)構(gòu)的is_waiting屬性值為true,表示當前事務需要等待,我們把這個場景就稱之為獲取鎖失敗,或者加鎖失敗,或者沒有成功的獲取到鎖。

在事務T1提交之后,就會把該事務生成的鎖結(jié)構(gòu)釋放掉,然后看看還有沒有別的事務在等待獲取鎖,發(fā)現(xiàn)了事務T2還在等待獲取鎖,所以把事務T2對應的鎖結(jié)構(gòu)的is_waiting屬性設置為false,然后把該事務對應的線程喚醒,讓它繼續(xù)執(zhí)行,此時事務T2就算獲取到鎖了。這種實現(xiàn)方式非常像并發(fā)編程里AQS的等待隊列。

對一條記錄加鎖的本質(zhì)就是在內(nèi)存中創(chuàng)建一個鎖結(jié)構(gòu)與之關聯(lián)。那么,一個事務對多條記錄加鎖時,是不是就要創(chuàng)建多個鎖結(jié)構(gòu)呢?比如:

SELECT * FROM teacher LOCK IN SHARE MODE;

很顯然,這條語句需要為teacher表中的所有記錄進行加鎖。那么,是不是需要為每條記錄都生成一個鎖結(jié)構(gòu)呢?其實理論上創(chuàng)建多個鎖結(jié)構(gòu)沒有問題,反而更容易理解。但是如果一個事務要獲取10,000條記錄的鎖,要生成10,000個這樣的結(jié)構(gòu),不管是執(zhí)行效率還是空間效率來說都是很不劃算的,所以實際上,并不是一個記錄一個鎖結(jié)構(gòu)。

當然鎖結(jié)構(gòu)實際是很復雜的,我們大概了解下里面包含哪些元素。

  • 鎖所在的事務信息:無論是表級鎖還是行級鎖,一個鎖屬于一個事務,這里記載著該鎖對應的事務信息。

  • 索引信息:對于行級鎖來說,需要記錄一下加鎖的記錄屬于哪個索引。

  • 表鎖/行鎖信息:表級鎖結(jié)構(gòu)和行級鎖結(jié)構(gòu)在這個位置的內(nèi)容是不同的。具體表現(xiàn)為表級鎖記載著這是對哪個表加的鎖,還有其他的一些信息;而行級鎖記載了記錄所在的表空間、記錄所在的頁號、區(qū)分到底是為哪一條記錄加了鎖的數(shù)據(jù)結(jié)構(gòu)。

  • 鎖模式:鎖是IS,IX,S,X 中的哪一種。
    鎖類型:表鎖還是行鎖,行鎖的具體類型。

  • 其他:一些和鎖管理相關的數(shù)據(jù)結(jié)構(gòu),比如哈希表和鏈表等。

基本上來說,同一個事務里,同一個數(shù)據(jù)頁面,同一個加鎖類型的鎖會保存在一起。

以上就是“MySQL知識點之InnoDB中的行級鎖是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI