前言
InnoDB 支援多粒度鎖(multiple granularity locking)
,它允許行級鎖
與表級鎖
共存,而意向鎖就是其中的一種表鎖
。
意向鎖(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;
複製程式碼
事務 A
獲取了users
表上的意向排他鎖。事務 A
獲取了 id 為 6 的資料行上的排他鎖。
之後事務 B
想要獲取 users
表的共享鎖:
LOCK TABLES users READ;
複製程式碼
事務 B
檢測到事務 A
持有users
表的意向排他鎖。事務 B
對users
表的加鎖請求被阻塞(排斥)。
最後事務 C
也想獲取 users
表中某一行的排他鎖:
SELECT * FROM users WHERE id = 5 FOR UPDATE;
複製程式碼
事務 C
申請users
表的意向排他鎖。事務 C
檢測到事務 A
持有users
表的意向排他鎖。- 因為意向鎖之間並不互斥,所以
事務 C
獲取到了users
表的意向排他鎖。 - 因為id 為 5 的資料行上不存在任何排他鎖,最終
事務 C
成功獲取到了該資料行上的排他鎖。
總結
- InnoDB 支援
多粒度鎖
,特定場景下,行級鎖可以與表級鎖共存。 - 意向鎖之間互不排斥,但除了 IS 與 S 相容外,
意向鎖會與 共享鎖 / 排他鎖 互斥
。 - IX,IS是表級鎖,不會和行級的X,S鎖發生衝突。只會和表級的X,S發生衝突。
- 意向鎖在保證併發性的前提下,實現了
行鎖和表鎖共存
且滿足事務隔離性
的要求。