MySQL鎖

Gouden發表於2021-03-06

MySQL鎖的型別

這裡講到的MySQL鎖和鎖的型別都是基於InnoDB來講的。

共享鎖和排他鎖

共享鎖(shared lock,簡稱S)允許多個讀操作同時進行不相互影響,排他鎖(exclusive lock,簡稱X)會阻塞其它排他鎖請求,直到當前釋放了鎖。

意向鎖

意向鎖是InnoDB為了支援多個粒度上的鎖定提出的概念,可以讓行級鎖和表級鎖共存。

意向鎖分為兩種,一種是意向共享鎖(intension shared lock,簡稱IS),一種是意向排他鎖(intension exclusive lock,簡稱IX):

  • IS表示一個事務要在這個表加S行鎖
  • IX表示一個事務要在這個表加X行鎖

比如select ... lock in share lock會加IS鎖,select ... for update會加IX鎖。

在一個事務獲取一張表S行鎖之前,必須對這張表加IS鎖或更高強度的鎖,在一個事務獲取一張表X行鎖之前,必須對這張表加IX鎖,表級別的鎖互斥關係如下所示:

 

 結合下面例子理解互斥關係:

 

在上面例子裡,現開啟一個事務加了S行鎖,然後對錶加X鎖,會發現對錶加X鎖被阻塞了,結合上面表,可以分析在加S行鎖的時候,給這張表加了一個IS表鎖,這樣在加X表鎖的時候,因為X表鎖和IS表鎖是互斥的,就導致加X表鎖的操作被阻塞了。

總而言之,意向鎖的目的就是為了加行鎖的時候,在表鎖維度有個標識,表示當前表正在進行一些操作,在加真正的表鎖的時候,就可以識別這個標識,判斷要加的表鎖是否能夠加上。

記錄鎖

記錄鎖又稱為行鎖,是InnoDB專門提供的一種鎖,上面例子裡,select * from MyBooks where id = 0 lock in share mode就會加上一個行鎖。

行鎖會在增、刪、改操作裡自動加上,在查詢操作,可以顯示加上lock in share mode或for update加上。

行鎖只會對索引行生效,有幾個點需要注意一下:

  • 命中的索引必須是主鍵或者唯一鍵索引
  • 如果SQL語句沒有使用索引或者優化器決定不用索引,那麼就會鎖全表

間隙鎖

間隙鎖是對索引記錄之間、第一個索引記錄之前或者最後一個索引記錄之後的鎖定,無論這個範圍內有沒有這個值,都會被鎖定,間隙可能跨過單個索引值、多個索引值甚至為空。

間隙鎖有幾個需要注意的點:

  • 如果一個條件命中唯一索引,不會使用間隙鎖
  • 如果明確只查詢一條資料,但是沒有命中唯一索引,會使用間隙鎖鎖定該條索引鍵之前和之後的間隙
  • 間隙鎖的目的是不讓在這個間隙裡插入資料,間隙鎖之間是不排他的,也就是說可以對同一個間隙加多個間隙鎖
  • 間隙鎖在讀提交級別下不生效

假如有表test,有兩個字端,欄位id是唯一鍵索引,欄位num是普通索引,有資料:

當事務A對3進行加鎖時,事務B插入(7, 2),(7, 4)會失敗,因為間隙鎖把num索引(1, 3),(3, 5)間隙鎖住了,這時插入(7, 6)是會成功的。

插入(7, 2)是指插入id=7,num=2。 

  

 

臨鍵鎖

臨鍵鎖是記錄鎖和間隙鎖的結合,InnoDB預設會命中臨鍵鎖,這時會所這條索引鍵之前和之後的間隙,同時會鎖這條索引鍵。

在上面的例子下,事務B插入(8, 3)會被阻塞。

記錄鎖、間隙鎖和臨鍵鎖之間的關係是什麼樣的?

丁奇提的兩個原則、兩個優化和一個Bug有利於理解這三種鎖之間的關係:

  • 原則1: 加鎖的基本單位是臨鍵鎖
  • 原則2: 查詢過程中訪問到的物件才會加鎖
  • 優化1: 索引上的等值查詢,給唯一鍵加鎖時,會退化成行鎖,注意這裡需要有行記錄
  • 優化2: 索引上的等值查詢,向右遍歷且最後一個值不滿足條件時,退化成間隙鎖
  • Bug: 唯一索引上的範圍查詢會訪問到不滿足條件的第一個值為止

具體可見https://time.geekbang.org/column/intro/139

插入意圖鎖

插入意圖鎖的作用是提高插入操作的併發。如果有多個事務的插入操作等待一段間隙被釋放,會在等待排他鎖時加上插入意圖鎖,等到間隙鎖被釋放時,這多個插入操作不會相互阻塞。

自增鎖

自增鎖是個表級別的鎖,專門處理插入操作自增列,一個事務在執行插入操作時,別的事務的插入操作會阻塞,知道這個插入操作執行完畢,這樣這個插入操作的自增列的資料是連續的。

相關文章