InnoDB中的鎖

InnoDB 中的鎖

本文翻譯自mysql5.6官方文檔:InnoDB Lockinghtml

本文介紹InnoDB包含的鎖的種類:mysql

  • 共享鎖(Shared Lock)和 排他鎖(Exclusive Lock)
  • 意向鎖(Intention Locks)
  • Record Locks
  • Gap Locks 間隙鎖
  • Next-Key Locks
  • Insert Intention Locks
  • AUTO-INC Locks

共享鎖和排他鎖

InnoDB實現了兩種類型的行級鎖:shared(S) locks、exclusive(X) locks。算法

  • 共享鎖(S):容許事務讀取一行數據
  • 排他鎖(X):容許事務更新或刪除一行數據

若是事務T1在記錄r上持有共享鎖,那麼其餘事務T2對記錄r的鎖請求處理方式以下:sql

  • 事務T2能夠當即得到請求的S鎖。結果是T1和T2在記錄r上都得到了S鎖。
  • 事務T2不能當即得到請求的X鎖。

若是事務T1在行r上得到了X鎖,事務T2在行r上請求的任何一種類型的鎖都不能當即得到。反而,事務T2不得不等待T1釋放在行r上的X鎖。併發

意向鎖(Intention Locks)

InnoDB支持多粒度(multiple granularity)鎖定,這種鎖定容許行級鎖和表級鎖共存。爲了在實際中支持多粒度加鎖操做,InnoDB使用了一種附加類型的鎖,稱爲意向鎖。在InnoDB中意向鎖是表級鎖。spa

InnoDB使用了兩種類型的意向鎖(假設事務T已請求表t上指定類型的鎖):翻譯

  • 意向共享鎖(IS): 事務T打算得到表t上某幾行的共享鎖。
  • 意向排他鎖(IX): 事務T打算得到表t上某幾行的排他鎖。

例如,SELECT ... LOCK IN SHARE MODE會請求IS鎖,SELECT ... FOR UPDATE會請求IX鎖。code

意向鎖的協議以下:orm

  • 一個事務在表的某一行上得到S鎖以前,它必須先在這個表上得到IS鎖或者更強的鎖。
  • 一個事務在表的某一行上得到X所以前,它必須先在這個表上得到IX鎖。

這些規則能夠總結以下:htm

X IX S IS
X 衝突 衝突 衝突 衝突
IX 衝突 兼容 衝突 兼容
S 衝突 衝突 兼容 兼容
IS 衝突 兼容 兼容 兼容

若是事務請求的鎖跟已經存在的鎖兼容,則事務會獲得它請求的鎖,若跟已存在的鎖有衝突,則不會得到它請求的鎖。事務會一直等待直到存在衝突的鎖被釋放。若是一個鎖請求跟已存在的鎖有衝突而且不能得到,則會產生死鎖。

Record Locks

Record Lock是一個在索引記錄上的鎖。例如,SELECT c1 FOR UPDATE FROM t WHERE c1 = 10; 防止其餘事務在行t.c1 = 10上的插入、刪除和更新操做。

Record locks老是鎖定索引記錄,即便表中沒有一個索引。這種狀況下,InnoDB會使用隱式的聚簇索引來進行鎖定。

間隙鎖(Gap Locks)

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

間隙可能會跨越單個索引值,多個索引值,甚至空值。

當使用惟一索引查詢惟一行時不須要使用間隙鎖。(這不包含此種狀況,查詢條件只包含多列惟一索引的一部分字段;這種狀況下仍是會用到間隙鎖。)例如,若是id列上有惟一索引,下面的語句僅對id等於100的行使用索引記錄鎖,不會關心其餘會話是否在間隙以前插入行:

SELECT * FROM child WHERE id = 100;

若id字段未加索引或者爲非惟一索引,此語句會鎖住id=100記錄前面的間隙。

值得注意的是經過不一樣的事務相互衝突的鎖能夠持有同一個間隙。例如,事務A在一個間隙上持有共享間隙鎖(gap S-lock),同時事務B能夠在此間隙上持有排他間隙鎖(gap X-lock)。這種狀況被容許的緣由是:若是一個記錄從索引上被清除,則此記錄上被不一樣事務持有的間隙鎖必須合併。

在InnoDB中,間隙鎖是「徹底被抑制(purely inhibitive)」的,也就是說它只會阻止其餘事務在間隙中進行插入操做。它不會阻止不一樣的事務在同一間隙上獲取間隙鎖。所以,排他間隙鎖跟共享間隙鎖具備相同的效果。

當事務隔離級別設置爲 READ COMMITTED 或者 啓用系統變量innodb_locks_unsafe_for_binlog時,間隙鎖能夠被顯示禁用。在這種狀況下,間隙鎖在查詢和索引掃描時會被禁用,僅在外鍵約束檢查和惟一性檢查時啓用。

當事務隔離級別設置爲READ COMMITTED或者啓用系統變量innodb_locks_unsafe_for_binlog時還有其餘效果。MySQL在評估完WHERE條件後針對不匹配的行會釋放記錄鎖。對於UPDATE語句,InnoDB作了半一致性(semi-consistent)讀,它會返回最後提交的版本到MySQL,以便MySQL能夠決定這行是否跟UPDATE語句的WHERE條件匹配。

Next-Key Locks

Next-Key Lock是索引記錄上的record lock與索引記錄以前間隙上的gap lock的組合。

當InnoDB搜索或掃描表的索引時會使用行級鎖,會在它遇到的索引記錄上設置共享鎖或排他鎖。所以,行級鎖就是索引記錄鎖。索引記錄上的next-key lock也會對此索引記錄以前的間隙產生影響。也就是說,next-key lock是索引記錄鎖加上該索引記錄以前間隙上的間隙鎖。若是一個會話在索引的記錄R上持有一個共享鎖或排他鎖,那麼另外一個會話不能在R以前的間隙上當即插入一個新的索引記錄。

假設一個索引包含10,11,13和20這四個值,那麼該索引的next-key locks可能覆蓋以下區間:

(負無窮, 10]
(10, 11]
(11, 13]
(13, 20]
(20, 正無窮)

對於最後一個區間,next-key lock鎖住的是索引中大於最大值的間隙(鎖住的僅僅是索引中最大值以後的間隙)。上確界是一個大於索引中任何一個記錄的僞記錄。上確界並非一個真實的索引記錄,所以,事實上next-key lock僅僅鎖住的是最大索引值以後的間隙。

默認狀況下,InnoDB運行在REPEATABLE READ事務隔離級別而且系統變量innodb_locks_unsafe_for_binlog 未啓用。在這種狀況下,InnoDB使用next-key lock搜索和掃描索引,避免了幻影行(phantom rows)

Insert Intention Locks

插入意向鎖是在插入行以前經過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 STATUS來查看插入意向鎖的信息,TRANSACTIONS標題下的信息以下:

mysql> SHOW ENGINE INNODB STATUS\G  
...
SHOW ENGINE INNODB STATUS
---TRANSACTION 8731, ACTIVE 7 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7f996beac700, query id 30 localhost root update
INSERT INTO child (id) VALUES (101)
------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED:
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 Locks

自增加鎖是一個特殊的表級鎖,事務用此鎖爲AUTO_INCREMENT字段插入數據。最簡單的狀況下,若是一個事務正在往表中插入數據,那麼另外一個事務的插入操做必須等待,以致於第一個事務的插入操做可以得到連續的主鍵值。

innodb_autoinc_lock_mode配置選項可以控制自增加鎖的算法。它可讓你在自增加值的可預測序列和最大併發插入操做之間作權衡。更多信息查看: AUTO_INCREMENT Handling in InnoDB

具體分析加鎖例子可查看何成登的博文:MySQL 加鎖處理分析

參考:

相關文章
相關標籤/搜索