主要翻譯文檔 https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
針對部分理論知識自己做了測試。
InnoDB鎖類型主要包含以下幾種
- Shared and Exclusive Locks ,即我們常說的共享鎖和獨佔鎖
- Intention Locks,意向鎖
- Record Locks,行鎖
- Gap Locks,GAP鎖
- Next-Key Locks, Next-Key鎖
- Insert Intention Locks,插入意向鎖
- AUTO-INC Locks,AUTO-INC鎖
- Predicate Locks for Spatial Indexes,空間索引的Predicate Locks
Shared and Exclusive Locks
InnoDB實現了標準的行級鎖,其中有兩種類型的鎖:共享鎖和獨佔鎖。
- 共享鎖(S)允許事務在讀取一行記錄時加鎖
- 獨佔鎖(X)允許事務更新和刪除一行記錄。
S鎖語法:
select * from test where id = 1 lock in share mode;
X鎖語法:
select * from test where id = 1 for UPDATE;
如果事務T1持有r行的s鎖,然後事務T2針對r行的鎖遵從以下規則:
- T2申請s鎖時可以立即加鎖,針對r行,T1和T2可以同時持有s鎖
- T2申請x鎖時,需要等待T1釋放鎖
如果T1持有r行的x鎖,則T2申請x鎖和s鎖時均需要等待T1釋放鎖。
如下例子,具體ReadCommitted隔離級別:
T1 | T2 |
---|---|
START TRANSACTION; | START TRANSACTION; |
select * from test where id = 1 lock in share mode; | |
select * from test where id = 1 lock in share mode; |
以上事務執行成功。
T1 | T2 |
---|---|
START TRANSACTION; | START TRANSACTION; |
select * from test where id = 1 lock in share mode; | |
select * from test where id = 1 for UPDATE; |
以上發生鎖等待,T2 要等待T1釋放鎖後才正常執行。
以類似的例子,如果T1針對一行數據持有X鎖後,T2事務無法加任何鎖,再次不做演示。
Intention Locks 意向鎖
InnoDB支持多粒度鎖定,即允許行鎖和表鎖共存,比如: LOCK TABLES … WRITE 語句持有一個表的X鎖。爲了支持多粒度鎖定,InnoDB引擎採用意向鎖,意向鎖(Intention Locks)是一個表級別的鎖,用來指出一個事務將對錶中的某行數據加X鎖或S鎖。有兩種意向鎖:
- intention shared lock (IS),意向共享鎖,表明一個事務即將對錶中的某幾行記錄加S鎖。
- intention exclusive lock (IX),意向排他鎖,表明一個事務即將對錶中的某幾行記錄加X鎖。
比如 SELECT … LOCK IN SHARE MODE 增加 IS 鎖, SELECT … FOR UPDATE 增加 IX鎖。
意向鎖的條件如下:
- 在一個事務將對一行數據加S鎖前,他必須先對錶增加IS鎖。
- 在一個事務將對一行數據加X鎖前,他必須先對錶增加IX鎖。
表級鎖的衝突如下:
X | IX | S | IS | |
---|---|---|---|---|
X | Conflict | Conflict | Conflict | Conflict |
IX | Conflict | Compatible | Conflict | Compatible |
S | Conflict | Conflict | Compatible | Compatible |
IS | Conflict | Compatible | Compatible | Compatible |
意向鎖主要是想解決,一個事務想要對錶中的某行數據加鎖,提前避免衝突。
SHOW ENGINE INNODB STATUS 語句可以看到在InnoDB引擎下某行出下意向鎖:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
本地沒有重現意向鎖。。。。。
Record Locks 行鎖
行鎖是在索引記錄上的鎖。比如 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 語句就會阻止其他事務插入、更新或者刪除在t表的c1爲10的記錄上。
行鎖總是鎖定索引記錄,即使一個表沒有索引,InnoDB引擎也會創建一個默認的索引列。
SHOW ENGINE INNODB STATUS 語句可以看到行鎖發生的場景:
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 ;;
Gap Locks 間隙鎖
間隙鎖就是鎖住索引之間的記錄,或者鎖住第一個索引前或者最後一個索引後的記錄。比如語句:
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
組織其他事務插入t.c1爲15的記錄,不管這行是否存在數據,gap鎖都會生效。
GAP鎖可能跨越一個索引,或者一個索引範圍,設置是空值。
GAP鎖時性能和併發的權衡,只能在部分隔離級別下生效。間隙鎖是在可重複讀(repeatable read)級別下會產生。
GAP鎖不需要執行的sql語句使用唯一索引查詢一個唯一行,比如針對一個id爲100的記錄,不會產生GAP
SELECT * FROM child WHERE id = 100;
id不是索引字段或者不具有唯一索引,則會產GAP鎖。
間隙所之前不衝突,比如事務A持有行記錄r的間隙所,事務B仍然可以繼續持有。比如如下:
TA | TB |
---|---|
START TRANSACTION; | START TRANSACTION; |
select * from test where id < 5 AND id > 1 for UPDATE; | |
select * from test where id < 7 AND id > 5 for UPDATE; |
但是,事務B再執行插入語句 insert into test VALUES(5, ‘5’, ‘5555’); 時,會發生鎖等待。
GAP鎖可以被禁用,當我們設置事務隔離界級別爲ReadCommitted,或者開啓 innodb_locks_unsafe_for_binlog 參數。
Next-Key Locking
Next-Key鎖是行鎖和索引之前的間隙鎖的組合。
Insert Intention Locks 插入意向鎖
插入意向鎖也是一個間隙鎖,在對行記錄的插入前加鎖。插入意向鎖主要是爲了解決多個事務同時插入數據到相同的索引區間,當他們插入的是不同的位置,但是區間相同,則無需彼此等待。
比如針對一個空表,我們插入兩行記錄
insert into test VALUES(7, '7', '5555');
insert into test VALUES(9, '9', '5555');
事務A執行操作:
START TRANSACTION;
select * From test where id > 7 for update;
然後事務B執行插入操作時會被阻塞:
START TRANSACTION;
insert into test VALUES(8, '5', '5555');
但是事務B卻可以正常的執行更新操作而無需獲取鎖
update test set user_id = 8 where id = 8;
AUTO-INC Locks
Auto-inc鎖時一個表級鎖,在插入AUTO_INCREMENT列時產生。
Predicate Locks for Spatial Indexes
,空間索引的Predicate Locks,在RR級別或者 SERIALIZABLE 下,Next-key 鎖無法有效保證空間索引的正常,所以產生此鎖。