InnoDB存儲引擎支持表鎖和行鎖。顧名思義,表鎖是鎖住整張表,行鎖只是鎖住某些行。InnoDB經過給索引項加鎖來實現行鎖,若是沒有索引,則經過隱藏的聚簇索引來對記錄加鎖。若是操做不經過索引條件檢索數據,InnoDB 則對錶中的全部記錄加鎖,實際效果就和表鎖同樣。InnoDB存儲引擎有3種行鎖的算法,分別是:算法
以下圖所示,數據庫
例如一個索引有10,11,13,20這四個值。InnoDB能夠根據須要使用Record Lock將10,11,13,20四個索引鎖住,也可使用Gap Lock將(-∞,10),(10,11),(11,13),(13,20),(20, +∞)五個範圍區間鎖住。Next-Key Locking相似於上述兩種鎖的結合,它能夠鎖住的區間有爲(-∞,10],(10,11],(11,13],(13,20],(20, +∞),能夠看出它即鎖定了一個範圍,也會鎖定記錄自己。服務器
InnoDB存儲引擎的鎖算法的一些規則以下所示,後續章節會給出對應的實驗案例和詳細講解。性能
在不經過索引條件查詢時,InnoDB 會鎖定表中的全部記錄。你們能夠登陸上本身的MySQL服務器,親自試驗一下。優化
試驗發現,會話二的查詢操做真的是會發生等待。那麼,這句話真的是對的嗎?注意必須在會話二操做還在等待時進行查詢,不然查詢不到。ui
其中locktrxid爲1851的事務是會話二的事務,另外一個是會話一的事務。咱們能夠看到兩個鎖都要對值爲1的主鍵索引加鎖。須要注意的是,這裏是對主鍵進行加鎖。兩者之間的關係是怎麼肯定的呢?咱們能夠經過 information_schema.INNODB_LOCK_WAITS中的數據肯定。spa
奇怪,不是說好的鎖定表中的全部記錄嘛?查找了不少資料,發現 INNODB_LOCKS的定義以下:線程
The INNODB_LOCKS table contains information about each lock that an InnoDB transaction has requested but not yet acquired, and each lock that a transaction holds that is blocking another transaction.3d
也就是說,這張表並不會顯示全部鎖的信息,而是隻顯示要申請卻沒有申請到,和已經持有鎖而且阻塞其餘線程的鎖信息。怪不得必須在會話二進行等待時進行查詢才能查獲得數據。orm
由於兩個會話的操做都要鎖住全部的行,因此發現每次在第一行記錄上就發生了鎖等待。那咱們使用插入語句試試。表e1的主鍵a的值爲1-4,咱們分別插入主鍵爲1-4(固然會有主鍵重複問題,可是因爲有鎖,一直等待)的新記錄,分別查詢鎖信息,就能看到會話一的事務對全部的主鍵都加了鎖,也就是對全部的記錄都加了鎖。
InnoDB存儲引擎的行鎖是經過鎖住索引實現的,而不是記錄。這是理解不少數據庫鎖問題的關鍵。
因爲InnoDB特殊的索引機制,數據庫操做使用主鍵索引時,InnoDB會鎖住主鍵索引;使用非主鍵索引時,InnoDB會先鎖住非主鍵索引,再鎖定主鍵索引。不瞭解InnoDB索引機制的能夠參考這篇文章
以下圖所示,當InnoDB鎖定非主鍵索引b時,它也會鎖住其對應的主鍵索引,因此鎖住b值爲2和3的非主鍵索引,那麼與其相關的a值爲6,5的主鍵索引也須要被鎖住。
好比說,一種常見的死鎖狀況通常出如今以下圖所示的操做場景中。
會話一的語句使用了b上的索引,由於它是非主鍵索引,因此會先在b索引上添加鎖,再去a索引上加鎖。而會話二的語句偏偏相反,會先在索引a上加鎖,再去索引b加鎖。這種狀況下,就可能出現死鎖。
默認隔離級別REPEATABLE-READ下,InnoDB中行鎖默認使用算法Next-Key Lock,只有當查詢的索引是惟一索引或主鍵時,InnoDB會對Next-Key Lock進行優化,將其降級爲Record Lock,即僅鎖住索引自己,而不是範圍。當查詢的索引爲輔助索引時,InnoDB則會使用Next-Key Lock進行加鎖。InnoDB對於輔助索引有特殊的處理,不只會鎖住輔助索引值所在的範圍,還會將其下一鍵值加上Gap LOCK。
廢話很少說,咱們來看一下相關的實驗,先作一下準備。
CREATE TABLE e4 (a INT, b INT, PRIMARY KEY(a), KEY(b)); INSERT INTO e4 SELECT 1,1; INSERT INTO e4 SELECT 3,1; INSERT INTO e4 SELECT 5,3; INSERT INTO e4 SELECT 7,6; INSERT INTO e4 SELECT 10,8;
而後開啓一個會話執行下面的語句。
SELECT * FROM e4 WHERE b=3 FOR UPDATE;
由於經過索引b來進行查詢,因此InnoDB會使用Next-Key Lock進行加鎖,而且索引b是非主鍵索引,因此還會對主鍵索引a進行加鎖。對於主鍵索引a,僅僅對值爲5的索引加上Record Lock(由於以前的規則)。而對於索引b,須要加上Next-Key Lock索引,鎖定的範圍是(1,3]。除此以外,還會對其下一個鍵值加上Gap Lock,即還有一個範圍爲(3,6)的鎖。
你們能夠再新開一個會話,執行下面的SQL語句,會發現都會被阻塞。
SELECT * FROM e4 WHERE a = 5 FOR UPDATE; # 主鍵a被鎖 INSERT INTO e4 SELECT 4,2; # 插入行b的值爲2,在鎖定的(1,3]範圍內 INSERT INTO e4 SELECT 6,5; # 插入行b的值爲5,在鎖定的(3,6)範圍內
InnoDB引擎採用Next-Key Lock來解決幻讀問題。由於Next-Key Lock是鎖住一個範圍,因此就不會產生幻讀問題。可是須要注意的是,InnoDB只在Repeatable Read隔離級別下使用該機制。