InnoDB鎖機制(locking)


本文將描述InnoDB用到的鎖類型。
1)共享和排他鎖(Shared and Exclusive Locks)。
2)意向鎖(Intention Locks)。
3)記錄鎖( Record Locks)。
4)間隙鎖(Gap Locks)。
5)下一鍵值鎖(Next-Key Locks)。
6)插入意向鎖(Insert Intention Locks)。
7)自動插入鎖( AUTO-INC Locks)。
8)空間索引謂詞鎖(Predicate Locks for Spatial Indexes)。


1.共享鎖(S)和排他鎖(X)
InnoDB實現標準的行級鎖,其中,分爲兩種鎖,共享鎖和排他鎖。
1)共享鎖允許持有共享鎖的事務讀取一行數據。
2)排他鎖允許持有排他鎖的事務更新或刪除一行數據。如果事務T1在數據行r上持有一個共享鎖,那麼,另一個請求在數據行r上獲取一個鎖的不同事務被做如下處理:
  --事務T2請求獲取一個共享鎖能被立刻被授予。結果,事務T1和T2在數據行r上都持有共享鎖。
  --事務T2請求獲取一個排他鎖不能立刻被授予。如果事務T1在數據行r上持有一個排他鎖,某個不同事務T2在數據行r上的任何模式的鎖都不能被立刻授予。相反,事務T2必須等待事務T1釋放數據行r上的鎖。


2.意向鎖
InnoDB支持多個粒度的鎖,從而允許行鎖和表鎖的共存。例如:一個像lock table ... where的語句獲取特定表上的排他鎖。爲了實現多粒度級別的鎖,InnoDB用意向鎖。意向鎖爲表級鎖,其表示一個事務稍後將獲取的表中數據行上的鎖模式。有兩種意向鎖模式:
1)意向共享鎖(IS)表示一個事務打算在表的單獨數據行上設置共享鎖。
2)意向排他鎖(IX)表示一個事務打算在表的單獨數據行上設置排他鎖。
例如:select ... lock in share mode設置一個意向共享鎖,而select ... for update則設置一個意向排他鎖。
意向鎖協議如下所示:
1)事務在獲取表中數據行上的共享鎖前,必須先獲取表上的意向共享鎖或更強的鎖。
2)事務在獲取表中數據行上的排他鎖前,必須先獲取表上的意向排他鎖。
表級鎖模式間的兼容性總結如下:
           X           IX          S          IS
X          Conflict    Conflict    Conflict   Conflict
IX         Conflict    Compatible  Conflict   Compatible
S          Conflict    Conflict    Compatible Compatible
IS         Conflict    Compatible  Compatible Compatible
如果被事務請求的鎖與已經存在的鎖兼容,則被授予,但如果與已經存在的鎖衝突,則不被授予。事務將一直等待到存在衝突的已存在鎖被釋放。如果請求的鎖與已存在鎖衝突而不能被授予,從而導致死鎖而報錯。
除了全表請求(例如:lock tables ... write)外,意向鎖並不阻塞任何事情。意向鎖的主要目的是顯示某人正鎖定一個數據行,或將要鎖定表中的一個數據行。
意向鎖的事務數據顯示與下列show engine innodb status和InnoDB監視器輸出類似:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX


3.記錄鎖
記錄鎖是索引記錄上的一個鎖。例如:select c1 from t where c1=10 for update;阻止任何其他事務插入,更新或刪除t.c1爲10的數據行。
記錄鎖總是鎖定索引記錄,即使表上沒有定義索引。對這種情況,InnoDB創建一個隱式簇索引,並用該索引進行記錄鎖定。
記錄鎖的事務數據便是與下列show engine innodb status和InnoDB監視器輸出類似:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc 'O;;
2: len 7; hex b60000019d0110; asc ;;


4.間隙鎖
間隙鎖是索引記錄之間間隙上的鎖,或第一個索引記錄之前或最後一個索引記錄之後間隙上的鎖。例如:select c1 from t where c1 between 10 and 20 for update;阻止其他事務將15數值插入t.c1,而不管該列中是否已有該值,因爲該範圍內的所有已存在值間的間隙已經被鎖定。
一個間隙也許跨越單個索引值,多個索引值,或甚至爲空。
間隙鎖是性能和併發間的權衡,被用於某些事務隔離級別而非其他隔離級別。
用唯一索引鎖定數據行來查找唯一數據行的語句不需要間隙鎖。(這並不包括查找條件只包含多列唯一索引中部分列的場景;這種場景,確實會發生間隙鎖。)例如:如果id列有一個唯一索引,下面的語句只用一個索引記錄鎖鎖定id值爲100的數據行,其並不關心其他會話是否在前面的間隙插入數據行:
select * from child where id=100;
如果id列上沒建索引,或建有非唯一索引,那麼該SQL確實會鎖定前述的間隙。
這裏,也值得注意的是,不同的事務能在一個間隙上持有相互衝突的鎖。例如:事務A能在一個間隙上持有一個共享間隙鎖(gap S-lock),而事務B在同樣的間隙上持有一個排他間隙鎖(gap X-lock)。允許衝突間隙鎖的原因是如果一個索引記錄被purge掉,不同事務在該記錄上的間隙鎖必須被合併。InnoDB中的間隙鎖是“純粹禁止的(purely inhibitive)”,這意味着它們只是阻止其他事務往指定間隙插入數據。它們並不阻止不同事務在同一個間隙上獲取間隙鎖。這樣,間隙鎖X-lock和間隙鎖S-lock有同樣的效果。
用戶可以顯式的關閉間隙鎖。如果將事務隔離級別改爲read committed或開啓innodb_locks_unsafe_for_binlog系統變量(現已被棄用)時,就可以關閉間隙鎖。這種場景下,間隙鎖對查找和索引掃描關閉,而僅用於外鍵約束檢查和重複鍵檢查。
使用read committed隔離級別或開啓innodb_locks_unsafe_for_binlog也會有其他影響。mysql評估了where條件後,將會釋放不匹配數據行上的記錄鎖。對update語句,InnoDB進行一個“半一致”讀,這樣,InnoDB將向mysql返回最近被提交的版本,因此,mysql能決定數據行是否與update語句的where條件匹配。


5.下一鍵值鎖(Next-Key Locks)
下一鍵值鎖是索引記錄的記錄鎖與該索引記錄前間隙上間隙鎖的組合。
InnoDB查找或掃描一個表索引時,會以這種方式執行行級鎖定,其在遇到的索引記錄上放置共享或排他鎖。這樣,行級鎖實際上是索引記錄鎖。索引記錄上的下一鍵值鎖頁影響該索引記錄前的“間隙”。也就是,下一鍵值鎖時一個索引記錄鎖加上一個該索引記錄前間隙上的間隙鎖。如果一個會話在索引記錄R上有一個共享或排他鎖,另一個會話不能將一個新的索引記錄立即插入索引記錄R前的間隙中。
假設索引包含值10,11,13和20。該索引可能的下一鍵值鎖將覆蓋下列間隔,這裏,一個圓括號表示不包括間隔端點,而一個方括號表示包括間隔端點。
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
對最後的間隔,下一鍵值鎖鎖定索引最大值與“上確界(supremum)”僞記錄間的間隙,這裏,“上確界”僞記錄爲大於索引中實際存在的任何值的一個數值。上確界不是一個真實的索引記錄,因此,實際上,該下一鍵值鎖僅鎖定最大索引值後的間隙。默認地,InnoDB按照repeatable read事務隔離級別進行操作。因此,InnoDB使用下一鍵值鎖進行查找和索引掃描,從而阻止幻影行的發生。
下一鍵值鎖事務數據看上去類似下面show engine innodb status和InnoDB監視器輸出:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc 'O;;
2: len 7; hex b60000019d0110; asc ;;


6.插入意向鎖(Insert Intention Locks)
插入意向鎖是一種數據行插入前insert操作設置的間隙鎖。該鎖表示,當多個事務往相同索引間隙的不同位置插入數據時,這些事務無需相互等待。假設有值爲4和7的索引記錄。不同事務分別想要插入值5和6,每個事務在獲得插入行的排他鎖前,都用插入意向鎖鎖定4和7間的間隙,但因爲它們插入的數據行並不衝突,因此它們並不會相互阻塞。
下面的例子說明一個事務獲得插入記錄的排他鎖前獲得了一個插入意向鎖。該例子涉及兩個客戶端,A和B。
客戶端A創建包含兩個索引記錄(90和102)的一張表,接着開始了一個事務,該事務在ID大於100的索引就上放置了一個排他鎖。排他鎖包含一個記錄102前的間隙鎖:
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id |
+-----+
| 102 |
+-----+
客戶端B開始了一個往前述間隙插入一條記錄的事務。該事務等待獲取一個排他鎖時獲取了一個插入意向鎖。
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);
插入意向鎖事務數據看上去類似下面show engine innodb status和innodb監視器輸出:
RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000066; asc f;;
1: len 6; hex 000000002215; asc " ;;
2: len 7; hex 9000000172011c; asc r ;;...

7.自增鎖(AUTO-INC Locks)
自增鎖是一種向帶有auto_increment列的表中插入數據時獲取的特殊的表級鎖。最簡單的場景中,如果一個事務正向表中插入數值,其他事務往該表中插入數據的操作必須等待,以便第一個事務插入的數據行能得到連續的主鍵值。
innodb_autoinc_lock_mode配置選項控制用於自增鎖的算法。其允許用戶選擇如何在插入操作自增值的可預期順序與最大併發間權衡。


8.空間索引謂詞鎖(Preicate Locks for Spatial Indexes)
InnoDB支持在包含空間數據的列上創建空間索引。
爲了處理設計空間索引操作的鎖定,下一鍵值鎖支持repeatable read或serializable事務隔離級別時運行並不好。多維數據中並沒有絕對的順序概念,因此,哪個是“下一個”鍵值並不清晰。
爲了支持建有空間索引表的隔離級別,InnoDB使用了謂詞鎖。一個空間索引包含最小邊界矩形(minimum bounding rectangle,MBR)值,因此,InnnoDB通過在查詢所用的MBR上放置一個謂詞鎖,從而強制該索引上的一致性讀。期間,其他事務不能插入或修改匹配該查詢條件的數據行。
 

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