一 鎖類型
InnoDB支援的鎖類型包括:
共享鎖和排它鎖(Shared and Exclusive Locks)
意向鎖(Intention Locks)
記錄鎖(Record Locks)
間隙鎖(Gap Locks)
臨鍵鎖(Next-Key Locks)
插入意向鎖(Insert Intention Locks)
自增鎖(AUTO-INC Locks)
空間索引的謂詞鎖(Predicate Locks for Spatial Indexes)
二 較長的描述
2.1 共享鎖和排它鎖
InnoDB實作了标準的行級鎖,有兩種類型:共享鎖(S鎖) 和 排它鎖(X鎖)。
1、共享鎖允許持有鎖的事務讀取行;
2、排它鎖允許持有鎖的事務更新或删除一行。
如果事務T1持有行r上的一個共享鎖,那麼來自某個不同僚務T2的對r行的鎖的請求處理如下:
1、事務T2發出的擷取S鎖的請求可以被立即通過。是以事務T1和T2都持有 r上的S鎖
2、事務T2發出的擷取X鎖的請求不會被立即通過
如果事務T1持有一個行r上的排它(X)鎖,另一個事務T2無論讀寫請求都不會立即通過。而是T2需要等待T1釋放在行r上的鎖。
2.2 意向鎖
InnoDB支援多粒度鎖,允許行鎖和表鎖共存。例如,一個類似 LOCK TABLES... WRITE的語句持有某張表上的一個X鎖。為了實作多粒度級别的鎖定,InnoDB使用了意向鎖。意向鎖是表級鎖,用于訓示事務稍後對表中的行需要哪種類型的鎖(共享或獨占)。意向鎖有兩種類型:
1、意向共享鎖(IS)表示事務打算在表中的各個行上設定共享鎖。
2、意向排他鎖(IX)表示事務打算對表中的各個行設定排他鎖。
例如,SELECT ... FOR SHARE設定一個IS(意向共享鎖)鎖,而 SELECT ... FOR UPDATE 設定了一個IX(意向排它鎖)鎖。
意向鎖協定如下:
1、在事務可以擷取表中某一行的共享鎖之前,它必須首先擷取表上的IS鎖或更強的鎖
2、在事務可以擷取表中行的排他鎖之前,它必須首先擷取表上的IX鎖。
下表總結了表級鎖類型相容性(Conflict表示沖突,Compatible表示相容):

如果請求事務與現有鎖相容,則将鎖授予該事務,但如果該事務與現有鎖沖突,則不授予該事務。事務将等待沖突的現有鎖被釋放。如果鎖請求與現有鎖沖突,并且由于它會導緻死鎖而無法授予,則會發生錯誤。
意向鎖不會阻止除完整表請求以外的任何請求(例如,LOCK TABLES ... WRITE)。意向鎖的主要目的是顯示有人正在鎖定一行,或者将要鎖定表中的一行。
意向鎖的事務資料在SHOW ENGINE INNODB STATUS和INNODB 監控器輸出中顯示如下:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
2.3 記錄鎖
記錄鎖是在一條索引記錄上的鎖。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 防止任何其他事務對t.c1值為10的記錄進行插入、更新或删除操作。
記錄鎖始終鎖定索引記錄,即使定義的表沒有索引。在這種情況下,InnoDB建立一個隐藏的聚簇索引并使用這個索引用于記錄鎖。
記錄鎖的事務資料在SHOW ENGINE INNODB STATUS和INNODB 螢幕輸出中顯示如下:
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 ;;
2.4 間隙鎖(Gap locks)
間隙鎖是在索引記錄之間間隙上的鎖,或在第一個索引記錄之前或最後一個索引記錄之後對間隔的鎖定。例如,
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
語句,組織其他事物插入15到列t.c1中,無論在列中是否已經存在15這個值,這是因為所有現有值中間範圍的間隙都已經被鎖定(指針對上面between 10 到 20的這個範圍)。
間隙可以跨越單個索引值、多個索引值,甚至可以是空的。
間隙鎖是性能和并發性之間折衷的一部分,在某些事務隔離級别中使用,而在其他級别中不會使用。
對于使用唯一索引鎖定行以搜尋唯一行的語句,不需要間隙鎖定(這不包括搜尋條件隻包括多列唯一索引的某些列的情況;在這種情況下,會發生間隙鎖定)。 例如,如果id列具有唯一索引,則下面的語句僅對id值為100的行使用索引記錄鎖,并且與其他會話是否在前面的間隔中插入行無關:
SELECT * FROM child WHERE id = 100;
如果id沒有索引或者沒有唯一索引,那麼這條語句将鎖住前面的間隙。這裡還值得注意的是,沖突的鎖可以由不同的事務保留在間隙上。例如,事務A可以持有一個間隙上的共享間隙鎖(gap S-lock),與此同時事務B持有相同間隙上的一個排他間隙鎖(gap X-lock)。允許存在沖突的間隙鎖的原因是,如果從索引中清除記錄,則必須合并由不同僚務保留在記錄上的間隙鎖。
InnoDB中的Gap鎖是“純抑制性的”,這意味着它們的唯一目的是防止其他事務插入到Gap中。間隙鎖可以共存。一個事務擷取的間隙鎖并不阻止另一個事務在同一間隙上擷取間隙鎖。共享和獨占間隙鎖之間沒有差別。它們彼此不沖突,并且執行相同的功能。
可以明确禁用間隙鎖定。如果将事務隔離級别更改為READ COMMITTED(讀已送出),則會發生這種情況。在這些情況下,對搜尋和索引掃描禁用間隙鎖定,并且僅用于外鍵限制檢查和重複鍵檢查。
使用READ-COMMITTED隔離級别還有其他效果。在MySQL評估WHERE條件之後,将釋放不比對行的記錄鎖。
對UPDATE語句,InnoDB執行“半一緻”讀取,這樣它會将最新送出的版本傳回給MySQL,以便MySQL可以确定行是否比對UPDATE語句的WHERE條件。
2.5 臨鍵鎖(Next-Key Locks)
臨鍵鎖是索引記錄上一個記錄鎖和這條索引記錄前面間隙上的間隙鎖的組合。
InnoDB執行行級鎖定的方式是,當它搜尋或掃描表索引時,它會對遇到的索引記錄設定共享或排他鎖。是以,行級鎖實際上是索引記錄鎖。索引記錄上的下一個鍵鎖也會影響該索引記錄之前的“間隙”。也就是說,臨鍵鎖是索引記錄鎖加上索引記錄前面的間隔上的間隔鎖。如果一個會話對索引中的記錄R具有共享或排它鎖,則另一個會話不能在緊靠索引順序中的R之前的間隙中插入新的索引記錄。假設索引包含值10、11、13和20。此索引的臨鍵鎖可能包含以下區間,其中圓括号表示排除邊界點,方括号表示包含邊界點:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
對于最後一個間隙,臨鍵鎖鎖定索引中最大值上方的間隙和“上确界”僞記錄,該僞記錄的值高于索引中的任何實際值。上确界不是真正的索引記錄,是以,實際上,臨鍵鎖隻鎖定最大索引值後面的間隙。
預設情況下,InnoDB的隔離級别是REPEATABLE READ(可重複讀)。這種情況下,InnoDB使用臨鍵鎖進行搜尋和索引掃描,進而防止幻讀(幻影行)。
臨鍵鎖的事務資料在SHOW ENGINE INNODB STATUS和INNODB 螢幕輸出中顯示如下:
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 ;;
2.6 插入意向鎖
插入意向鎖是在插入行之前由插入操作設定的一種間隙鎖。此鎖表示插入的意圖,如果插入到同一索引間隙中的多個事務不在間隙中的同一位置插入,則它們不需要等待對方。假設存在值為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 STATUS和INNODB monitor輸出中的顯示類似于下面的内容:
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 ;;...
2.7 自增鎖(AUTO-INC Locks)
自增鎖是一種特殊的表級鎖,由插入到具有自動增量列的表中的事務産生。在最簡單的情況下,如果一個事務正在向表中插入值,則任何其他事務都必須等待自己向該表中插入值,以便第一個事務插入的行接收連續的主鍵值。
innodb_autoinc_lock_mode配置項控制用于自動增量鎖定的算法。它允許我們選擇如何在可預測的自動增量值序列和插入操作的最大并發性之間進行權衡。
2.8 空間索引謂詞鎖(Predicate Locks for Spatial Indexes)
InnoDB支援包含空間列的空間索引。為了處理涉及空間索引操作的鎖定,臨鍵鎖不能很好地支援可重複讀 或 序列化事務隔離級别。多元資料中沒有絕對的排序概念,是以不清楚哪個是“下一個”鍵。
為了支援具有空間索引的表的隔離級别,InnoDB使用謂詞鎖。空間索引包含最小邊界矩形(MBR)值,是以InnoDB通過對用于查詢的MBR值設定謂詞鎖來強制索引的一緻讀取。其他事務不能插入或修改與查詢條件比對的行。
三 總結
本章整理了InnoDB中的鎖類型,和鎖的含義及簡單示例。在後續的文章中,我們會詳細闡述分析每一種鎖。