MySQL InnoDB鎖類型

引子

在上一篇文章《MySQL事務的特性與隔離級別》中,我寫了MySQL事務的特性以及隔離級別。接下來,我跟着官方文檔,總結一下 MySQL InnoDB中鎖的類型。

共享鎖與排它鎖

InnoDB實現了標準的行鎖,包括兩種:共享鎖(Shard Locks,簡寫爲 S lock)和排它鎖(Exclusive Locks,簡寫爲 X lock)。

  • 共享鎖允許持有該鎖的事務讀取一行,同時會阻止其他事務獲取該行的排它鎖。
  • 排它鎖允許持有該所的事務更新或刪除一行,同時會阻止其他事務獲取該行的排它鎖。

意向鎖

InnoDB支持多粒度的鎖,允許行鎖和表鎖並存。爲了在多個粒度級別上實現鎖定,InnoDB實現了 意向鎖(Intention Locks)。意向鎖是表鎖,表示事務稍後要對錶中的某一行使用哪種類型的鎖(共享鎖和排它鎖)。有兩種類型的意向鎖:意向共享鎖(Intention Shard Lock,簡稱 IS lock) 和 意向排它鎖(Intention Exclusive Locks,簡稱爲 IX lock)。

  • 意向共享鎖 表示事務打算對錶中的個別行設置 共享鎖。
  • 意向排它鎖 表示事務打算對錶中的個別行設置 排他鎖。

意向鎖的使用方式是:

  • 在事務準備獲取某行的共享鎖之前,它必須先獲取該表的意向共享鎖或更強的鎖;
  • 在事務準備獲取某行的排它鎖之前,它必須先獲取該表的意向排他鎖或更強的鎖。

意向鎖的主要目的是表明有人正在鎖定表中的行,或者打算鎖定表中的行。也就是說,除了全表請求(例如,LOCK TABLES ... WRITE 會在指定表上申請一個排它鎖),意向鎖不會阻止其他內容。

例如,SELECT ... FOR SHARE 會設置一個 意向共享鎖(IS lock)SELECT ... FOR UPDATE 會設置一個 意向排它鎖(IX lock)

記錄鎖

記錄鎖(Record Locks)是對索引記錄的鎖定。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 會防止其他事務更新、插入或刪除 t.c1 = 10 的行。

記錄鎖總是鎖定索引記錄,即使對於沒有定義索引的表也是如此。對於這種情況,InnoDB創建一個隱藏的聚簇索引(clustered index)並將該索引用於記錄鎖定。

間隙鎖

對於間隙鎖,首先介紹一下間隙(gap)的概念。下面是官方文檔中的描述:

A place in an InnoDB index data structure where new values could be inserted. 

間隙(gap)就是 InnoDB的索引數據結構中,可以插入新值的地方。大家把上面這段官方文檔原文多讀兩遍,把下文的內容看完,大家一定能夠理解。

間隙鎖(Gap Locks)是對索引記錄之間的間隙的鎖定,或者是對 第一個索引記錄之前 或 最後一個索引記錄之後 的鎖定。下面是官方文檔原文:

A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.

比如,對於 SELECT c1 FROM t WHERE c1 BETWEEN 10 AND 15 FOR UPDATE; 如果有其他事務想要插入或更新 c1 = 13 的記錄,都會被阻止,不管這條記錄是否已經存在。

對於間隙鎖,間隙中可能只包含一條索引記錄,也可能包含多條索引記錄,甚至可能是空的。

間隙鎖是在性能和併發性之間權衡的結果,只能用於一些事務隔離級別。

對於使用唯一索引來鎖定唯一一行的語句,不需要使用到間隙鎖。這不包括搜索條件只包括 複合唯一索引(multiple-column unique index) 中的一些列的情況,此時仍然會鎖定間隙。

比如:SELECT * FROM t WHERE id = 100; 這樣一條語句,如果 id 列具有唯一索引,則只會對 id = 100 的這一行記錄使用 索引記錄鎖,而不會對其前後的間隙進行鎖定。如果 id 列未建立索引或具有非唯一索引,則會對上文提到的間隙進行鎖定。

對於間隙鎖,還需要注意的是:不同的事務可以在同一個間隙上持有衝突的鎖。例如,事務A可以在一個間隙上持有一個共享的間隙鎖(gap S-lock),而事務B可以在同一個間隙上持有一個獨佔的間隙鎖(gap X-lock)。允許存在衝突的間隙鎖的原因是,如果從索引中刪除一條記錄,不同的事務持有的記錄上的間隙鎖將會被合併。最後這句話什麼意思,我覺得可以用下面這段話來解釋——

InnoDB中的間隙鎖是“完全禁止的”(purely inhibitive),它存在的唯一目的是爲了防止其他事務插入數據到鎖定的間隙中。間隙鎖可以共存,一個事務持有的間隙鎖不會阻止其他事務對同一間隙的鎖定。共享間隙鎖 和 排他間隙鎖 之間沒有什麼不同,它們的功能也是一樣的——都是爲了防止其他事物插入數據到鎖定的間隙中。

Next-Key Locks

Next-Key Lock 是 索引記錄上的記錄鎖 和 索引記錄前的間隙上的間隙鎖 的組合。

InnoDB執行 行鎖 的方式是,當它搜索或掃描表索引時,它會在遇到的索引記錄上設置 共享鎖或排它鎖。也就是說,行鎖 實際上是對 索引記錄 的鎖定。索引記錄上的 Next-Key Lock 還會影響該索引記錄之前的“間隙”。

如果一個會話在索引中的記錄R上具有共享鎖或排它鎖,則另一個會話不能在按照索引順序 緊鄰R之前的間隙中插入新的索引記錄。下面是官方文檔中的例子:

假設一個索引包含10、11、13和20這四個值。則該索引可能的 next-key lock 涵蓋了以下區間:

(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)

插入意向鎖

插入意向鎖(Insert intention Locks) 是一種在插入行之前由 INSERT 操作設置的間隙鎖。

該鎖以這樣一種方式發出插入意圖的信號:如果插入到 相同索引間隙 中的多個事務沒有在 間隙中的相同位置 插入,則它們不需要彼此等待。

假設有值爲 4 和7 的索引記錄,有兩個獨立的事務分別準備插入值 5 和 6。在未獲得插入行的排它鎖之前,每個事務都使用 插入意向鎖 來鎖定 4 和 7 之間的間隙,但它們不會彼此阻塞,因爲兩個事務插入的數據並不在同一行,這些行是彼此不衝突的。

AUTO-INC Locks

AUTO-INC Lock 是一種特殊的表級鎖,由插入到具有 AUTO-INCREMENT 列的表中的事務獲取。

在最簡單的情況下,如果一個事務 T 正在向表中插入值,則其他任何事務都必須等待 事務T 在表中插入完畢,以便 事務T 插入的行獲得連續的主鍵值。

Predicate Locks for Spatial Indexes

針對這個鎖,我初步看了下官方文檔,深感自己道行太淺,不懂的太多。先把上面的知識消化下,等理解的差不多了,再來看看這裏。

總結

知識是連貫的,原來就是想看看 MySQL 的隔離級別,看着發現,還需要了解下鎖的機制。看了鎖,發現還需要了解下MySQL的索引。知識是學不完的,學到知識的滿足感 就是我繼續學習的動力。學海無涯,任重道遠~

參考文檔

1、https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

2、https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_gap

3、https://www.jianshu.com/p/bf862c37c4c9

4、https://www.percona.com/blog/2012/03/27/innodbs-gap-locks/

5、https://www.cnblogs.com/zhoujinyi/p/3435982.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章