天天看點

mysql鎖機制之行鎖(四)

前言

顧名思義,行鎖就是一鎖鎖一行或者多行記錄,mysql的行鎖是基于索引加載的,是以行鎖是要加在索引響應的行上,即命中索引,如下圖所示:

InnoDB 支援​

​多粒度鎖(multiple granularity locking)​

​​,它允許​

​行級鎖​

​​與​

​表級鎖​

​共存,而意向鎖就是其中的一種​

​表鎖​

​。

上面我們找到行鎖是命中索引,一鎖鎖的是一張表的一條記錄或者是多條記錄,記錄鎖是在行鎖上衍生的鎖,我們來看看你記錄鎖的特征:

記錄鎖:記錄鎖鎖的是表中的某一條記錄,記錄鎖的出現條件必須是精準命中索引并且索引是唯一索引,如主鍵id,就像我們上面描述行鎖時使用的sql語句圖,在這裡就挺适用的。

間隙鎖又稱之為區間鎖,每次鎖定都是鎖定一個區間,隸屬行鎖。既然間隙鎖隸屬行鎖,那麼,間隙鎖的觸發條件必然是命中索引的,當我們查詢資料用範圍查詢而不是相等條件查詢時,查詢條件命中索引,并且沒有查詢到符合條件的記錄,此時就會将查詢條件中的範圍資料進行鎖定(即使是範圍庫中不存在的資料也會被鎖定),我們通過代碼示範一下:

首先,我們打開兩個視窗,在視窗A中我們根據id做一個範圍更改操作,不送出事務,然後在範圍B中插入一條記錄,該記錄的id值位于視窗A中的條件範圍内,我們看看運作效果:

意向鎖(Intention Locks)

需要強調一下,意向鎖是一種​

​不與行級鎖沖突表級鎖​

​,這一點非常重要。意向鎖分為兩種:

  • 意向共享鎖(intention shared lock, IS):事務有意向對表中的某些行加共享鎖(S鎖)
-- 事務要擷取某些行的 S 鎖,必須先獲得表的 IS 鎖。
SELECT column FROM table ... LOCK IN SHARE MODE;
複制代碼      
  • 意向排他鎖(intention exclusive lock, IX):事務有意向對表中的某些行加排他鎖(X鎖)
-- 事務要擷取某些行的 X 鎖,必須先獲得表的 IX 鎖。
SELECT column FROM table ... FOR UPDATE;
複制代碼      

即:​

​意向鎖是有資料引擎自己維護的,使用者無法手動操作意向鎖​

​,在為資料行加共享 / 排他鎖之前,InooDB 會先擷取該資料行所在在資料表的對應意向鎖。

意向鎖要解決的問題

我們先來看一下百度百科上對意向鎖存在意義的描述:

如果另一個任務試圖在該表級别上應用共享或排它鎖,則受到由第一個任務控制的表級别意向鎖的阻塞。第二個任務在鎖定該表前不必檢查各個頁或行鎖,而隻需檢查表上的意向鎖。

設想這樣一張 ​

​users​

​ 表: MySql,InnoDB,Repeatable-Read:users(id PK,name)

id name
1 ROADHOG
2 Reinhardt
3 Tracer
4 Genji
5 Hanzo
6 Mccree

事務 A 擷取了某一行的排他鎖,并未送出:

SELECT * FROM users WHERE id = 6 FOR UPDATE;
複制代碼      

事務 B 想要擷取 ​

​users​

​ 表的表鎖:

LOCK TABLES users READ;
複制代碼      

因為共享鎖與排他鎖​

​互斥​

​​,是以事務 B 在視圖對 ​

​users​

​ 表加共享鎖的時候,必須保證:

  • 目前沒有其他事務持有 users 表的排他鎖。
  • 目前沒有其他事務持有 users 表中任意一行的排他鎖 。

為了檢測是否滿足第二個條件,事務 B 必須在確定 ​

​users​

​表不存在任何排他鎖的前提下,去檢測表中的每一行是否存在排他鎖。很明顯這是一個效率很差的做法,但是有了意向鎖之後,情況就不一樣了:

意向鎖的相容互斥性

意向鎖是怎麼解決這個問題的呢?首先,我們需要知道意向鎖之間的相容互斥性:

意向共享鎖(IS) 意向排他鎖(IX)
意向共享鎖(IS) 相容 相容
意向排他鎖(IX) 相容 相容

即意向鎖之間是互相相容的,emmm......那你存在的意義是啥?

雖然意向鎖和自家兄弟互相相容,但是它會與普通的排他 / 共享鎖互斥:

意向共享鎖(IS) 意向排他鎖(IX)
共享鎖(S) 相容 互斥
排他鎖(X) 互斥 互斥

注意:這裡的排他 / 共享鎖指的都是表鎖!!!意向鎖不會與行級的共享 / 排他鎖互斥!!!

現在我們回到剛才 ​

​users​

​ 表的例子:

​事務 A​

​ 擷取了某一行的排他鎖,并未送出:

SELECT * FROM users WHERE id = 6 FOR UPDATE;
複制代碼      

此時 ​

​users​

​​ 表存在兩把鎖:​

​users​

​ 表上的意向排他鎖與 id 為 6 的資料行上的排他鎖。

事務 B 想要擷取 users 表的共享鎖:

LOCK TABLES users READ;
複制代碼      

此時​

​事務 B​

​​ 檢測事務 A 持有 ​

​users​

​ 表的意向排他鎖,就可以得知​

​事務 A​

​ 必然持有該表中某些資料行的排他鎖,那麼​

​事務 B​

​​ 對 ​

​users​

​ 表的加鎖請求就會被排斥(阻塞),而無需去檢測表中的每一行資料是否存在排他鎖。

意向鎖的并發性

這就牽扯到我前面多次強調的一件事情:

意向鎖不會與行級的共享 / 排他鎖互斥!!!

意向鎖不會與行級的共享 / 排他鎖互斥!!!

意向鎖不會與行級的共享 / 排他鎖互斥!!!

重要的話要加粗說三遍,正因為如此,意向鎖并不會影響到多個事務對不同資料行加排他鎖時的并發性(不然我們直接用普通的表鎖就行了)。

最後我們擴充一下上面 users 表的例子來概括一下意向鎖的作用(一條資料從被鎖定到被釋放的過程中,可能存在多種不同鎖,但是這裡我們隻着重表現意向鎖):

id name
1 ROADHOG
2 Reinhardt
3 Tracer
4 Genji
5 Hanzo
6 Mccree

​事務 A​

​ 先擷取了某一行的排他鎖,并未送出:

SELECT * FROM users WHERE id = 6 FOR UPDATE;
複制代碼      
  1. ​事務 A​

    ​​ 擷取了​

    ​users​

    ​ 表上的意向排他鎖。
  2. ​事務 A​

    ​ 擷取了 id 為 6 的資料行上的排他鎖。
LOCK TABLES users READ;
複制代碼      
  1. ​事務 B​

    ​​ 檢測到​

    ​事務 A​

    ​​ 持有​

    ​users​

    ​ 表的意向排他鎖。
  2. ​事務 B​

    ​​ 對​

    ​users​

    ​ 表的加鎖請求被阻塞(排斥)。
SELECT * FROM users WHERE id = 5 FOR UPDATE;
複制代碼      
  1. ​事務 C​

    ​​ 申請​

    ​users​

    ​ 表的意向排他鎖。
  2. ​事務 C​

    ​​ 檢測到​

    ​事務 A​

    ​​ 持有​

    ​users​

    ​ 表的意向排他鎖。
  3. 因為意向鎖之間并不互斥,是以​

    ​事務 C​

    ​​ 擷取到了​

    ​users​

    ​ 表的意向排他鎖。
  4. 因為id 為 5 的資料行上不存在任何排他鎖,最終​

    ​事務 C​

    ​ 成功擷取到了該資料行上的排他鎖。

總結

  1. InnoDB 支援​

    ​多粒度鎖​

    ​,特定場景下,行級鎖可以與表級鎖共存。
  2. 意向鎖之間互不排斥,但除了 IS 與 S 相容外,​

    ​意向鎖會與 共享鎖 / 排他鎖 互斥​

    ​。
  3. IX,IS是表級鎖,不會和行級的X,S鎖發生沖突。隻會和表級的X,S發生沖突。
  4. 意向鎖在保證并發性的前提下,實作了​

    ​行鎖和表鎖共存​

    ​​且​

    ​滿足事務隔離性​

    ​的要求。