【譯】MySQL InnoDB Locking(官方文檔)

本文介紹InnoDB使用的鎖類型,還包括mysql提供的全局鎖和表級鎖,這裏就把官網的InnoDB Locking這一節翻譯過來,額外增長了mysql提供的全局鎖和表級鎖,寫博客的主要目的是爲了備忘,也是爲了溫故知識。後續會理下事務,在各個不一樣的隔離級別下,事務分別會加上哪些鎖,會以實驗的形式整理出來。目前很久沒寫文章,都快頹廢了,後續堅持每週兩篇。mysql

1、共享鎖與獨佔鎖

InnoDB實現了標準的行級鎖,其中有兩種類型的鎖:共享(S)鎖和獨佔(X)鎖。算法

  • 共享(S)鎖容許持有該鎖的事務讀取一行。
  • 獨佔(X)鎖容許持有該鎖的事務更新或者刪除一行。

若是事務T1持有r這行的共享(S)鎖,則來自某個不一樣事務T2的請求對行r的鎖的處理以下:sql

  • 事務T2獲取行r共享(S)鎖的請求能夠當即被批准。結果,事務T1和T2都在行r上持有共享(S)鎖
  • 事務T2獲取行r獨佔(X)鎖的請求不能當即被批准。

若是事務T1持有r這行的獨佔(X)鎖,則不能當即批准來自某個不一樣事務T2的請求,請求r行上任何一種類型的鎖。相反,事務T2必須等待事務T1釋放對行r的鎖。接下來看下由於行鎖的獨佔和共享致使的死鎖,實驗環境Mysql5.7:數據庫

CREATE TABLE yangzai  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` int(255) NULL DEFAULT NULL,
  `b` int(255) NULL DEFAULT NULL,
  `c` int(255) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `un_a`(`a`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8
INSERT into yangzai(a,b,c) VALUES (1,2,3);複製代碼

語句的順序就是SQL的執行順序bash


會話A開啓事務,執行INSERT插入操做,a是惟一索引,會話A在插入這條數據時,會先使用a索引當前讀判斷是否惟一索引衝突,yangzai表已經預先初始化(1,2,3),會話A插入數據失敗,可是會話A仍是持有a索引的共享鎖(鎖的兩階段,只有在事務提交後鎖纔會釋放),會話B開始更新a=1這行數據,更新前,執行器會調用InnoDB引擎接口獲取a=1這行數據,而且在a=1索引上加獨佔鎖,會話B在a=1索引加獨佔鎖等待,由於a=1的共享鎖被會話A持有還未釋放併發


會話A開始執行更新語句,須要在a=1索引加上獨佔鎖,會話A在a=1索引加獨佔鎖等待,由於會話B也要在a=1索引加獨佔鎖,致使會話B等待會話A在a=1索引的共享鎖釋放,事務 A 和事務 B 在互相等待對方的資源釋放,就是進入了死鎖狀態。工具

當出現死鎖之後,有兩種策略:一種策略是,直接進入等待,直到超時。這個超時時間能夠經過參數 innodb_lock_wait_timeout 來設置。另外一種策略是,發起死鎖檢測,發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其餘事務得以繼續執行。將參數 innodb_deadlock_detect 設置爲 on,表示開啓這個邏輯。
性能



2、意向鎖

InnoDB支持多粒度鎖,容許行鎖和表鎖共存。例如,LOCK TABLES ... WRITE之類的語句在指定的表上採用獨佔鎖(X鎖)。爲了使多粒度級別的鎖定切實可行,InnoDB使用意向鎖。意向鎖是表級鎖,指示事務稍後須要對錶中的行使用哪一種類型的鎖(共享或獨佔)。有兩種類型的意向鎖:ui

  • 意向共享鎖(IS)表示事務打算對錶中的單個行上設置共享鎖。
  • 意向獨佔鎖(IX)表示事務打算對錶中的單個行設置獨佔鎖。

例如,SELECT ... FOR SHARE設置一個IS鎖,SELECT ... FOR UPDATE 設置一個IX鎖. 意向鎖的協議以下:spa

  • 事務在獲取表中某行上的共享鎖以前,必須先獲取表上的IS鎖或更強的IS鎖。
  • 事務在獲取表中某行上的獨佔鎖以前,必須先獲取該表的IX鎖。

下表彙總了表級鎖類型兼容性,即表鎖和表級意向鎖的兼容性。


若是請求事務與現有事務的鎖兼容,則會將鎖授予該事務,但若是它與現有鎖衝突,則不會授予該事務鎖。事務將一直等到衝突的現有鎖被釋放。若是鎖請求與現有鎖衝突,而且因爲會致使死鎖而沒法授予,則會發生錯誤。

意向鎖只會阻塞全表請求(好比LOCK TABLES ... WRITE/READ,若是要對一行設置共享鎖,LOCK TABLES ... WRITE的操做就會阻塞,意向鎖就是爲了使行鎖和表鎖同時共存,已經有一個事務對錶的一行已經加獨佔鎖,防止其餘事務在對錶加獨佔鎖)。意向鎖的主要目的是顯示某事務正在鎖定一行,或者將要鎖定表中的一行。

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

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX複製代碼

3、記錄鎖

記錄鎖是對索引記錄的鎖。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;防止其餘事務插入、更新或刪除t.c1的值爲10的行。 記錄鎖老是鎖定索引記錄,即便表定義時沒有索引。在這種狀況下,InnoDB會建立一個隱藏的彙集索引(主鍵索引),並使用這個索引來鎖定記錄。 記錄鎖的事務數據相似於下面的SHOW ENGINE INNODB STATUS或者 InnoDB monitor 的輸出:

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 ;;複製代碼

4、間隙鎖

間隙鎖是指索引記錄之間的間隙上的鎖,或在第一個或最後一個索引記錄以前或以後的間隙上的鎖。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;防止其餘事務將值15插入到t.c1列中,不管該列中是否存在任何此類值,由於該範圍中全部現有值之間的間隙已被鎖定。 間隙可能跨越單個索引值、多個索引值,甚至爲空。 間隙鎖是性能和併發性之間權衡的一部分,在某些事務隔離級別下而不是其餘事務隔離級別中使用。間隙鎖使用在RR(可重複讀事務隔離級別中,間隙鎖解決幻讀)。 使用惟一索引來搜索惟一行的鎖定行的語句不須要間隙鎖鎖定,由於使用惟一索引進行等值搜索不會出現幻讀。(這不包括搜索條件包含多列惟一索引的狀況;在這種狀況下,會發生間隙鎖定。)例如,若是id列具備惟一索引,則如下語句僅對id值爲100的行使用索引記錄鎖,並不影響而其餘會話是否插入:

SELECT * FROM child WHERE id = 100;複製代碼

若是id沒有索引或索引不是惟一索引,則語句會鎖定前面的間隙。 這裏還值得注意的是,不一樣的事務可能會在一個間隙中持有衝突的鎖。例如,事務A能夠再一個gap上持有一個共享的gap鎖(gap S-lock),而事務B在同一個gap上持有一個獨佔的gap鎖(gap X-lock)。容許衝突的間隙鎖的緣由是,若是從索引中清除記錄,則必須合併由不一樣事務保留在記錄上的間隙鎖。 InnoDB中的Gap鎖是「徹底禁止的」,這意味着它們的惟一目的是防止其餘事務插入到Gap中。間隙鎖能夠共存。一個事務執行的間隙鎖不會阻止另外一個事務對同一個間隙執行間隙鎖。共享鎖和獨佔鎖之間沒有區別。它們不會相互衝突,它們執行相同的功能。 能夠顯示禁用間隙鎖定。若是將事務隔離級別更改成READ COMMITTED,則會發生這種狀況。 能夠顯式禁用間隙鎖定。若是將事務隔離級別更改成READ COMMITTED,則會發生這種狀況。在這些狀況下,間隙鎖定在搜索和索引掃描中被禁用,而且僅用於外鍵約束檢查和重複鍵檢查。 使用READ COMMITTED隔離級別還有其餘效果。在MySQL評估WHERE條件以後,將釋放非匹配行的記錄鎖。對於UPDATE語句,InnoDB執行「(semi-consistent)半一致」讀取,這樣它會將最新提交的版本返回給MySQL,以便MySQL能夠肯定行是否與更新的WHERE條件匹配。

5、Next-Key Locks

一個next-key鎖是索引記錄上的記錄鎖和索引記錄以前的間隙鎖的組合。 InnoDB執行行級鎖的方式是,當它搜索或掃描表索引時,它會在遇到的索引記錄上設置共享鎖或獨佔鎖。所以,行級鎖其實是索引記錄鎖。索引記錄上的next-key鎖也會影響該索引記錄前面的間隙。也就是說,next-key鎖是索引記錄鎖加上索引記錄前面的間隙上的間隙鎖。若是一個會話對索引中的記錄R具備共享或獨佔鎖,則另外一個會話不能按索引順序在R前面的間隙中插入新的索引記錄。 假設索引包含值十、十一、13和20。此索引的next-key可能覆蓋如下間隔,其中一個圓括號表示間隔端點的排除,一個方括號表示端點的包含:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)複製代碼

對於最後一個間隔,下一個密鑰鎖將間隔鎖定在索引中最大值之上,而且「supremum」僞記錄的值高於索引中的任何實際值。上確界不是真正的索引記錄,所以,實際上,下一個鍵鎖只鎖定最大索引值後面的間隙。 默認狀況下,InnoDB在可重複讀事務隔離級別運行。在這種狀況下,InnoDB使用next-key鎖進行搜索和索引掃描,從而防止出現幻讀行。 next-key鎖的事務數據顯示相似於下面的SHOW ENGINE INNODB STATUS或者 InnoDB monitor 的輸出:

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 ;;複製代碼

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 ;;...複製代碼

7、自增鎖

AUTO-INC鎖是一種特殊的表級鎖,由事務插入具備AUTO_INCREMENT列的表中得到。在最簡單的狀況下,若是一個事務正在向表中插入值,則任何其餘事務都必須等待本身向該表中插入,以便第一個事務插入的行接收連續的主鍵值。 innodb_autoinc_lock_mode配置選項控制用於自動增量鎖定的算法。它容許您選擇如何在可預測的自動增量值序列和插入操做的最大併發性之間進行權衡。

8、Predicate Locks for Spatial Indexes

InnoDB支持對包含空間列的列進行空間索引。 爲了能處理涉及空間索引的操做的鎖定,next-key鎖不能很好地支持REPEATABLE READ 或 SERIALIZABLE的事務隔離級別。多維數據沒有絕對排序的概念,因此不清楚哪個是"下一個"鍵。 爲了支持具備空間索引的表的隔離級別,InnoDB使用predicate鎖。空間索引包含最小綁定矩形(MBR)值,所以InnoDB經過對用於查詢的MBR值設置predicate鎖來強制對索引進行一致讀取。其餘事務處理沒法插入或修改與查詢條件匹配的行。

9、全局鎖

全局鎖就是對整個數據庫實例加鎖。MySQL提供了一個加全局讀鎖的方法,命令是 Flush tables with read lock。使用這個命令讓整個庫處於只讀狀態的時候,以後其餘線程的如下語句會被阻塞:數據更新語句(數據的增刪改)、數據定義語句(包括建表、修改表結構、增長索引等)和更新類事務的提交語句。 全局鎖的典型場景是,作全庫邏輯備份。也就是把整庫每一個表都 select 出來存成文本,在備份的過程當中整個庫徹底處於只讀狀態:

  • 若是在主庫上備份,在備份期間都不能執行更新操做,業務上只能查詢,基本上停擺
  • 若是在從庫上備份,在備份期間從庫不能執行主庫同步過來的binlog,會致使主從延遲,若是大部分查詢都是走從庫,會致使查詢出來的數據不一致。

官方自帶的邏輯備份工具是 mysqldump。當 mysqldump 使用參數–single-transaction 的時候,導數據以前就會啓動一個事務,來確保拿到一致性視圖。而因爲 MVCC 的支持,這個過程當中數據是能夠正常更新的。一致性讀的前提是引擎要支持這個隔離級別。MyISAM這種不支持事務的引擎,若是備份過程有更新數據,只能取到最新數據,就破壞備份的一致性。就只能用全局鎖進行保證備份的一致性。 set global readonly=true也可使全庫只讀,readonly可讓全庫只讀,可是備份一致性仍是建議使用全局鎖:

  • readonly經常使用來判斷一個庫是主庫仍是備庫。
  • 客戶端異常處理機制上。全局鎖客戶端異常斷開,Mysql會自動釋放這個全局鎖,整庫回到能夠正常的更新。將整個庫設置爲 readonly 以後,若是客戶端發生異常,數據庫也會一直保持 readonly 狀態,會致使整個庫長時間處於不可寫狀態。

10、表級鎖

mysql表級別的鎖有兩種:一種是表鎖,一種是元數據鎖。 表鎖的語法是lock tables … read/write, 和全局鎖相似,能夠主動unlock tables釋放鎖,客戶端異常斷開也會自動釋放。MyISAM不支持行鎖,只支持最小粒度的表級鎖。lock tables除了會限制別的線程的讀寫外,也限定了本線程接下來的操做對象。舉個例子, 若是在某個線程 A 中執行 lock tables t1 read, t2 write; 這個語句,則其餘線程寫 t一、讀寫 t2 的語句都會被阻塞。同時,線程 A 在執行 unlock tables 以前,也只能執行讀 t一、讀寫 t2 的操做。連寫 t1 都不容許,天然也不能訪問其餘表。 元數據表級鎖,無需顯示加上,在訪問一個表的時候會被自動加上。防止DDL和DML在不一樣線程同時進行。若是一個查詢正在遍歷一個表中數據,而執行期間有其餘線程對這個表結構作變動,刪除一列,查詢線程獲取到的數據結果跟表結構對不上,絕對不容許。元數據表級鎖有兩種類型,元數據表級讀鎖,元數據表級寫鎖。

  • 元數據表級讀鎖不互斥,容許多個線程同時對同一張表進行增刪改查。
  • 元數據表級讀鎖寫鎖、寫鎖寫鎖互斥,保證表結構變動。若是存在多個線程同時給表加字段,要等待其餘線程執行完才能開始執行。


使用show processlist;查看鏈接信息


寫的不對的地方或很差的地方但願你們幫忙糾正下

相關文章
相關標籤/搜索