MySQL InnoDB 鎖——官方文檔

我的認爲學習MySQL最好的書面材料莫過於官方文檔了,它不只詳細介紹了方方面面的使用方法,還講解了原理,讓你知其然而且知其因此然。這裏就把官網的InnoDB Locking這一小節翻譯過來,拋磚引玉。html

InnoDB鎖類型包括mysql

共享鎖與獨佔鎖

InnoDB 實現了標準的行級鎖,包括兩種:共享鎖(簡稱 s 鎖)、排它鎖(簡稱 x 鎖)算法

  • 共享鎖容許持鎖事務讀取一行
  • 排它鎖容許持鎖事務更新或者刪除一行

若是事務 T1 持有行 r 的 s 鎖,那麼另外一個事務 T2 請求 r 的鎖時,會作以下處理:sql

  • T2 請求 s 鎖當即被容許,結果 T1 T2 都持有 r 行的 s 鎖
  • T2 請求 x 鎖不能被當即容許

若是 T1 持有 r 的 x 鎖,那麼 T2 請求 r 的 x、s 鎖都不能被當即容許,T2 必須等待T1釋放 x 鎖才行。併發

意向鎖

InnoDB 支持多粒度的鎖,容許表級鎖和行級鎖共存。一個相似於 LOCK TABLES ... WRITE 的語句會得到這個表的 x 鎖。爲了實現多粒度鎖,InnoDB 使用了意向鎖(簡稱 I 鎖)。I 鎖是代表一個事務稍後要得到針對一行記錄的某種鎖(s or x)的對應表的表級鎖,有兩種:性能

  • 意向排它鎖(簡稱 IX 鎖)代表一個事務意圖在某個表中設置某些行的 x 鎖
  • 意向共享鎖(簡稱 IS 鎖)代表一個事務意圖在某個表中設置某些行的 s 鎖

例如, SELECT ... LOCK IN SHARE MODE 設置一個 IS 鎖, SELECT ... FOR UPDATE 設置一個 IX 鎖。
意向鎖的原則以下:學習

  • 一個事務必須先持有該表上的 IS 或者更強的鎖才能持有該表中某行的 S 鎖
  • 一個事務必須先持有該表上的 IX 鎖才能持有該表中某行的 X 鎖

各個鎖的兼容性以下:spa

X IX S IS
X N N N N
IX N Y N Y
S N N Y Y
IS N Y Y Y

新請求的鎖只有兼容已有鎖才能被容許,不然必須等待不兼容的已有鎖被釋放。一個不兼容的鎖請求不被容許是由於它會引發死鎖,錯誤會發生。
意向鎖只會阻塞全表請求(好比 LOCK TABLES ... WRITE )。意向鎖的主要目的是展現某人正在鎖定表中一行,或者將要鎖定一行。翻譯

意向鎖的事務數據相似於下面的 SHOW ENGINE INNODB STATUS或者 InnoDB monitor 的輸出:code

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

記錄鎖

記錄鎖針對索引記錄。舉個例子, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 阻止其餘全部事務插入、修改或者刪除 t.c1 是 10 的行。
記錄鎖老是鎖定索引記錄,即便表沒有索引(這種狀況下,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        ;;

間隙鎖

間隙鎖(gap)是索引記錄之間上的鎖,或者說第一個索引記錄以前或最後一個索引記錄以後的間隔上的鎖。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 阻止其餘事務插入 t.c1 = 15 的記錄,不論是否已經有這種值在本列中,由於這個範圍內的全部值都被上鎖了。

一個間隙多是一個索引值、多個索引值,甚至是空的。

間隙鎖是性能與併發的部分折中,並只適用於一些事務隔離級別。

使用惟一索引的時候用不上間隙鎖。例如,id 列有惟一索引,下面的語句只是用索引記錄鎖(針對id=100的行)無論其餘會話是否在前面的間隙中插入行。

SELECT * FROM child WHERE id = 100;

若是id列沒有索引或者是非惟一索引,那麼這條語句的確會鎖住前面的間隙。

一樣值得注意的是,不一樣的事務可能會在一個間隙中持有衝突的鎖,例如,事務A能夠持有一個間隙上共享的間隙鎖(gap s lock)同時事務B持有
該間隙的排他的間隙鎖(gap x lock),衝突的間隙鎖被容許的緣由是若是一條記錄從索引中被清除了,那麼這條記錄上的間隙鎖必須被合併。

間隙鎖在Innodb中是被「十足的抑制」的,也就是說,他們只阻止其餘事務插入到間隙中,他們不阻止其餘事物在同一個間隙上得到間隙鎖,因此 gap x lock 和 gap s lock 有相同的做用。

間隙鎖能夠被顯式的關閉,好比你能夠:一、設置事務隔離級別爲讀已提交或者二、把 innodb_locks_unsafe_for_binlog 系統變量設置爲 true (如今已經廢棄這個變量了)。這兩種狀況下間隙鎖就被關閉了,索引掃描只用於外鍵檢查和重複鍵檢查。

上面兩個操做還要其餘效果。mysql檢索了where條件後,不匹配的行的記錄鎖釋放了,對於UPDATE語句,Innodb有一個semi-consistent 讀操做,亦即返回最新提交的版本給mysql,這樣mysql就能判斷哪些行符合條件。

Next-Key Locks

Next-Key Locks (簡稱 NK 鎖)是記錄鎖和間隙鎖的組合。
Innodb是這樣執行行級別鎖的,它搜索或者掃描一個表的索引,在他趕上的索引記錄上設置共享或者排他鎖,這樣行級鎖實際就是索引記錄鎖,一個NK 鎖一樣影響索引記錄以前的間隙。因此,NK 鎖是一個索引記錄鎖和索引記錄以前的間隙上的間隙鎖。若是一個會話在一行 R 上有一個共享、排它鎖,其餘會話不能當即在R以前的間隙中插入新的索引記錄。

假設一個索引包含值 10,11,13和20,索引上可能的NK 鎖包括以下幾個區間(注意開閉區間)

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

對於最後一個區間,NK 鎖鎖住了索引中最大值和比索引值中任何值都大的上確界僞值之上的間隙。上確界不是一個真正的索引記錄,因此事實上NK鎖只鎖住了最大索引值上的間隙。

默認狀況下,Innodb 是可重讀隔離級別,這樣的話,Innodb使用NK 鎖來進行索引搜索和掃描,阻止了幻讀

事務數據相似於下面:

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

插入意向鎖

插入意向鎖是在插入一行記錄操做以前設置的一種間隙鎖,這個鎖釋放了一種插入方式的信號,亦即多個事務在相同的索引間隙插入時若是不是插入間隙中相同的位置就不須要互相等待。假設有索引值四、7,幾個不一樣的事務準備插入五、6,每一個鎖都在得到插入行的獨佔鎖以前用插入意向鎖各自鎖住了四、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);

事務數據相似於下面:

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  ;;...

自增鎖

自增鎖是一個特殊的表級鎖,事務插入自增列的時候須要獲取,最簡單狀況下若是一個事務插入一個值到表中,任何其餘事務都要等待,這樣第一個事物才能得到連續的主鍵值。

innodb_autoinc_lock_mode配置選項控制了自增鎖的算法,它讓你選擇在可預測的連續自增值和併發度之間的平衡。

Section 14.8.1.5, 「AUTO_INCREMENT Handling in InnoDB」.

空間索引斷言鎖

InnoDB 支持針對含空間數據的列的列空間索引,要處理空間索引的鎖,next-key處理的很差,不能支持可重複讀序列化的事務隔離級別。由於多維數據中沒有絕對的順序概念,因此不能明確什麼是next key(下一個鍵)。

爲了支持含空間索引的表的事務隔離級別,InnoDB 使用了斷言鎖,一個空間索引包含了最小外接矩形(MBR)值,因此InnoDB 經過爲查詢使用的MBR設置斷言鎖保證索引了一致讀。其餘事務不能插入符合當前事務查詢條件的行。

查看翻譯原文:MageekChiu

相關文章
相關標籤/搜索