文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


17.7.1 InnoDB 鎖定

本節說明 InnoDB 使用的鎖定類型。

共用鎖定和獨佔鎖定

InnoDB 實作標準的列級鎖定,其中有兩種鎖定類型,共用 (S) 鎖定獨佔 (X) 鎖定

如果交易 T1 在列 r 上持有共用 (S) 鎖定,則來自某個不同交易 T2 對列 r 鎖定的請求將以如下方式處理

  • T2S 鎖定的請求可以立即授予。因此,T1T2 都會在 r 上持有 S 鎖定。

  • T2X 鎖定的請求無法立即授予。

如果交易 T1 在列 r 上持有獨佔 (X) 鎖定,則來自某個不同交易 T2r 上任何類型鎖定的請求都無法立即授予。相反地,交易 T2 必須等待交易 T1 釋放其在列 r 上的鎖定。

意圖鎖定

InnoDB 支援多重粒度鎖定,允許列鎖定和資料表鎖定共存。例如, LOCK TABLES ... WRITE 等陳述式會在指定的資料表上取得獨佔鎖定(X 鎖定)。為了使多重粒度級別的鎖定可行,InnoDB 使用意圖鎖定。意圖鎖定是資料表級鎖定,表示交易稍後在資料表中的列上需要哪種類型的鎖定(共用或獨佔)。有兩種意圖鎖定類型:

  • 意圖共用鎖定 (IS) 表示交易打算在資料表中的個別列上設定共用鎖定。

  • 意圖獨佔鎖定 (IX) 表示交易打算在資料表中的個別列上設定獨佔鎖定。

例如,SELECT ... FOR SHARE 設定 IS 鎖定,而 SELECT ... FOR UPDATE 設定 IX 鎖定。

意圖鎖定協定如下:

  • 在交易可以在資料表中的列上取得共用鎖定之前,它必須先在資料表上取得 IS 鎖定或更強的鎖定。

  • 在交易可以在資料表中的列上取得獨佔鎖定之前,它必須先在資料表上取得 IX 鎖定。

下表總結了表格層級鎖定的相容性。

X IX S IS
X 衝突 衝突 衝突 衝突
IX 衝突 相容 衝突 相容
S 衝突 衝突 相容 相容
IS 衝突 相容 相容 相容

如果請求的鎖與現有鎖相容,則授予該鎖給請求交易,否則若與現有鎖衝突則不授予。 交易會等待直到衝突的現有鎖被釋放。如果鎖定請求與現有鎖衝突,且因為會造成死鎖而無法授予,則會發生錯誤。

意向鎖不會阻止任何事物,除了完整的表格請求 (例如,LOCK TABLES ... WRITE)。意向鎖的主要目的是顯示有人正在鎖定一列,或是將要鎖定表格中的一列。

意向鎖的交易資料在 SHOW ENGINE INNODB STATUSInnoDB 監視器輸出中,看起來會類似如下:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

記錄鎖定

記錄鎖定是對索引記錄的鎖定。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 會阻止任何其他交易插入、更新或刪除 t.c1 值為 10 的列。

記錄鎖定始終鎖定索引記錄,即使表格定義時沒有索引。對於這種情況,InnoDB 會建立一個隱藏的叢集索引,並使用此索引進行記錄鎖定。請參閱第 17.6.2.1 節,「叢集索引和次要索引」

記錄鎖定的交易資料在 SHOW ENGINE INNODB STATUSInnoDB 監視器輸出中,看起來會類似如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

間隙鎖定

間隙鎖定是索引記錄之間間隙的鎖定,或是第一個索引記錄之前或最後一個索引記錄之後的間隙的鎖定。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 會阻止其他交易將值 15 插入到欄位 t.c1,無論該欄位中是否已存在任何此類值,因為該範圍內所有現有值之間的間隙都被鎖定。

間隙可能跨越單一索引值、多個索引值,甚至可能是空的。

間隙鎖定是效能與並行性之間的權衡,並在某些交易隔離層級中使用,而其他層級則不使用。

對於使用唯一索引搜尋唯一列來鎖定列的語法,不需要間隙鎖定。(這不包括搜尋條件僅包含多欄唯一索引的某些欄位的情況;在這種情況下,會發生間隙鎖定。) 例如,如果 id 欄位具有唯一索引,則以下語法僅對 id 值為 100 的列使用索引記錄鎖定,並且其他工作階段是否在先前的間隙中插入列並不重要

SELECT * FROM child WHERE id = 100;

如果 id 未建立索引或具有非唯一索引,則此語法會鎖定先前的間隙。

在這裡還值得注意的是,不同的交易可以對同一個間隙持有衝突的鎖定。例如,交易 A 可以對一個間隙持有共享間隙鎖定 (間隙 S 鎖定),而交易 B 可以對同一個間隙持有獨佔間隙鎖定 (間隙 X 鎖定)。允許衝突的間隙鎖定的原因是,如果從索引中清除記錄,則必須合併不同交易在該記錄上持有的間隙鎖定。

InnoDB 中的間隙鎖定是純粹抑制性的,這表示它們的唯一目的是防止其他交易插入到間隙中。間隙鎖定可以共存。一個交易取得的間隙鎖定不會阻止另一個交易在同一個間隙上取得間隙鎖定。共享和獨佔間隙鎖定之間沒有區別。它們彼此不衝突,並且執行相同的功能。

可以明確停用間隙鎖定。如果您將交易隔離層級變更為 READ COMMITTED,就會發生這種情況。在這種情況下,間隙鎖定會針對搜尋和索引掃描停用,並且僅用於外來鍵約束檢查和重複鍵檢查。

使用 READ COMMITTED 隔離層級也會有其他影響。在 MySQL 評估 WHERE 條件之後,會釋放不符列的記錄鎖定。對於 UPDATE 語法,InnoDB 會執行半一致讀取,使其將最新的已提交版本傳回給 MySQL,以便 MySQL 可以判斷列是否符合 UPDATEWHERE 條件。

下一鍵鎖定

下一鍵鎖定是索引記錄上的記錄鎖定,以及索引記錄之前的間隙上的間隙鎖定的組合。

InnoDB 執行列層級鎖定的方式是,當它搜尋或掃描表格索引時,會在它遇到的索引記錄上設定共享或獨佔鎖定。因此,列層級鎖定實際上是索引記錄鎖定。索引記錄上的下一鍵鎖定也會影響該索引記錄之前的間隙。也就是說,下一鍵鎖定是索引記錄鎖定加上索引記錄之前間隙上的間隙鎖定。如果一個工作階段在索引中記錄 R 上具有共享或獨佔鎖定,則另一個工作階段無法在索引順序中 R 之前的間隙中插入新的索引記錄。

假設索引包含值 10、11、13 和 20。此索引的可能下一鍵鎖定涵蓋以下間隔,其中圓括號表示排除間隔端點,而方括號表示包含端點

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

對於最後一個間隔,下一鍵鎖定會鎖定索引中最大值以上的間隙,以及值高於索引中任何實際值的至上虛擬記錄。至上不是真實的索引記錄,因此,實際上,此下一鍵鎖定僅鎖定最大索引值之後的間隙。

預設情況下,InnoDBREPEATABLE READ 交易隔離層級中運作。在這種情況下,InnoDB 會使用下一鍵鎖定進行搜尋和索引掃描,這會防止幻影列 (請參閱第 17.7.4 節,「幻影列」)。

下一鍵鎖定的交易資料在 SHOW ENGINE INNODB STATUSInnoDB 監視器輸出中,看起來會類似如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

插入意向鎖定

插入意向鎖定是由 INSERT 作業在插入列之前設定的一種類型間隙鎖定。此鎖定會發出在插入的意圖訊號,以便如果多個交易插入到同一個索引間隙,則如果它們不是插入在間隙中的相同位置,則不需要彼此等待。假設有值為 4 和 7 的索引記錄。嘗試分別插入值 5 和 6 的個別交易,會在取得插入列的獨佔鎖定之前,各自使用插入意向鎖定來鎖定 4 和 7 之間的間隙,但由於列不衝突,因此不會彼此封鎖。

以下範例示範了交易在取得插入記錄的獨佔鎖定之前,先取得插入意向鎖定。此範例包含兩個用戶端,A 和 B。

用戶端 A 建立一個包含兩個索引記錄 (90 和 102) 的表格,然後啟動一個交易,該交易在 ID 大於 100 的索引記錄上放置獨佔鎖定。獨佔鎖定包含記錄 102 之前的間隙鎖定

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

用戶端 B 開始一個交易,以將記錄插入到間隙中。此交易會在等待取得獨佔鎖定的同時,取得插入意向鎖定。

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

插入意向鎖定的交易資料在 SHOW ENGINE INNODB STATUSInnoDB 監視器輸出中,看起來會類似如下:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

自動遞增鎖定

AUTO-INC 鎖定是由交易插入具有 AUTO_INCREMENT 欄位的表格時取得的特殊表格層級鎖定。在最簡單的情況下,如果一個交易正在將值插入表格,則任何其他交易都必須等待才能對該表格執行自己的插入,以便第一個交易插入的列接收連續的主索引鍵值。

innodb_autoinc_lock_mode 變數控制用於自動遞增鎖定的演算法。它允許您選擇如何在可預測的自動遞增值序列和插入作業的最大並行性之間進行權衡。

如需更多資訊,請參閱第 17.6.1.6 節,「InnoDB 中的 AUTO_INCREMENT 處理」

空間索引的述詞鎖定

InnoDB 支援包含空間資料的欄位的 SPATIAL 索引 (請參閱第 13.4.9 節,「最佳化空間分析」)。

為了處理涉及 SPATIAL 索引的作業的鎖定,下一鍵鎖定無法很好地支援 REPEATABLE READSERIALIZABLE 交易隔離層級。多維資料中沒有絕對排序概念,因此不清楚哪個是下一個索引鍵。

為了對具有 SPATIAL 索引的表格啟用隔離級別的支援,InnoDB 使用謂詞鎖定。 SPATIAL 索引包含最小邊界矩形 (MBR) 值,因此 InnoDB 會對查詢所使用的 MBR 值設定謂詞鎖定,以此在索引上強制執行一致性讀取。其他交易無法插入或修改任何符合查詢條件的列。