1.一、lock和latch
mysql
1.二、主鍵索引和輔助索引
算法
1.三、事務的隔離級別
sql
2.一、主鍵索引如何加鎖
數據庫
2.二、輔助索引(非惟一索引)如何加鎖
session
1.一、lock和latch
併發
lock::lock的對象是事務,用來鎖定的是數據庫中的對象,如表,頁,行。(這個概念很重要,有助於理解後續的加鎖行爲)
學習
latch:latch是爲了保證併發線程操做臨界資源的正確性(本文能夠忽略這個概念)線程
1.二、主鍵索引和輔助索引3d
主鍵索引(彙集索引/聚簇索引): 每張表按照主鍵構造一棵b+樹,葉子節點存放的即爲整張表的行記錄數據rest
輔助索引:按輔助索引的鍵值構建b+樹,葉子節點的索引行中,除了包含索引的鍵值外,還包含一個書籤(bookmark),指向主鍵索引上該行的完整記錄
1.三、事務隔離級別
此處再也不贅述,本文會使用mysql默認的隔離級別,REPEATABLE READ(RR)
Innodb有三種行鎖的算法,分別是:
1⃣️、Record Lock:單個行記錄的鎖
2⃣️、Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄自己
3⃣️、Next-Key Lock:Record Lock + Gap Lock
下面咱們分析下,不一樣場景下的加鎖狀況,以及爲何要用對應的加鎖算法。
2.一、主鍵索引如何加鎖(mysql默認事務隔離級別:RR)
delete from t where id = 7; 這種狀況加鎖很簡單,因爲主鍵惟一,因此只須要在主鍵上id=7加X鎖便可。(Record Lock)
圖1-1
2.二、輔助索引(非惟一索引)如何加鎖(mysql默認事務隔離級別:RR)
select * from t where id=5 for update; 該場景下的加鎖以下圖所示。
圖1-2
從圖中能夠看出,本文除了在輔助索引和對應的主鍵索引上加了兩個行記錄的X鎖(Record Lock)外,還在輔助索引上加了3個GAP鎖。那麼爲何要用GAP鎖呢?其實這個是Innodb爲了解決幻讀問題(phantom problem)。你們想一下,爲了保證一個事務中的連續兩次讀(如:select * from t where id=5 for update)結果相同,那麼就要防止上鎖期間,有id=5的記錄插入表中,因爲id是非惟一索引,考慮到b+樹索引的連續性,因此,可以插入id=5記錄的,只有(3,5),(5,5),(5,9)三個區間,即上圖三個紅箭頭的位置範圍(上文1.1中提到lock的對象是數據庫中的對象,因此GAP鎖的的邊界會加在具體的索引記錄上,因此id=5的話,那就會在3和9之間加上GAP鎖。)
網上有些資料,包括我看的一些書上,對於Next-Key Lock的描述要麼模凌兩可,要麼淺嘗則止。這樣的描述很容易誤導剛剛入坑的同窗。大部分的資料描述的區間鎖範圍都是左閉右開。好比上文中select * from t where id=5 for update;那麼GAP LOCK的範圍就是 (3,5],(5,5],(5,9]。按照這個描述,那麼id=3的記錄不可插入,id=9的記錄能夠插入。那麼,事實是否是這樣的呢?
下面咱們一塊兒來嘗試一下,直接上圖:
首先咱們看下錶中的記錄(上文中的例子):
step1: sessionA ,發起一個事務,給記錄上鎖
插入id=3的兩條語句,一條失敗了,一條成功了,狀況好像跟咱們想的有點不同。。。 。 繼續上圖
插入id=9的兩條語句,又是一條失敗了,一條成功了。這個狀況,和Next-Key Lock的定義,左閉右開,貌似不同啊。
下面,咱們一塊兒來分析下,究竟是爲何?
要想搞明白這個問題,仍是得提到1.1章節中,加粗的那個lock的定義,它是加在索引上的。再結合索引的連續性,那麼這個問題就好理解了。
請你們把目光回到圖1-2,對於輔助索引葉子節點上的排序,能夠簡化爲該圖中的樣式。因此,GAP LOCK的範圍是((3,d) ,(5,c)),((5,c) ,(5,f)),((5,f) ,(9,g)),RECORD LOCK鎖定的是(5,c)和(5,f)。因此對於邊界值的插入就很清楚了哈
場景一、
mysql> insert into t(id,name) values(3,'f');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
(3,f) 在((3,d) ,(5,c))之間,不能插入。
場景二、
mysql> insert into t(id,name) values(3,'b');
Query OK, 1 row affected (0.00 sec)
(3,b) 在((3,d) ,(5,c))的左側,能夠插入。場景三、
mysql> insert into t(id,name) values(9,'e');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
(9,e) 在((5,f) ,(9,g))之間,不能插入。
場景四、
mysql> insert into t(id,name) values(9,'h');
Query OK, 1 row affected (0.00 sec)
(9,h) 在((5,f) ,(9,g))右側,能夠插入。