淺談Innodb的鎖實現

目錄

1、知識準備

1.一、lock和latch
mysql

1.二、主鍵索引和輔助索引
算法

1.三、事務的隔離級別
sql

2、Innodb行鎖的三種算法

2.一、主鍵索引如何加鎖
數據庫

2.二、輔助索引(非惟一索引)如何加鎖
session

3、發生死鎖的幾種狀況

4、我的小結


1、知識準備

1.一、lock和latch
併發

lock::lock的對象是事務,用來鎖定的是數據庫中的對象,如表,頁,行。(這個概念很重要,有助於理解後續的加鎖行爲
學習

latch:latch是爲了保證併發線程操做臨界資源的正確性(本文能夠忽略這個概念)線程

1.二、主鍵索引和輔助索引3d

主鍵索引(彙集索引/聚簇索引): 每張表按照主鍵構造一棵b+樹,葉子節點存放的即爲整張表的行記錄數據rest

輔助索引:按輔助索引的鍵值構建b+樹,葉子節點的索引行中,除了包含索引的鍵值外,還包含一個書籤(bookmark),指向主鍵索引上該行的完整記錄

1.三、事務隔離級別

此處再也不贅述,本文會使用mysql默認的隔離級別,REPEATABLE READ(RR)

2、Innodb行鎖的三種算法

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 ,發起一個事務,給記錄上鎖


setp2: sessionB,發起一個事務,插入邊界值。

插入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))右側,能夠插入。


3、發生死鎖的幾種狀況

3.一、顯示的互相等待(簡單場景)

3.一、隱式的互相等待(稍複雜場景)
還記得圖1-2中的場景嗎?輔助索引上加鎖後,會在對應的主鍵索引上加鎖。那麼當一張表中有多個輔助索引的時候,是否是會初現死鎖呢?這個場景做爲思考題,你們本身思考下哈 大笑

4、我的小結

本章的分享,其實就是我學習這塊知識遇到問題,以及解決問題的過程。想要理解innodb的加鎖原理,必需要理解b+樹和索引。固然了,遇到難題,不要輕易放棄哈,有時候用下逆向思惟,想下做者發明這個技術,是爲了解決什麼問題 大笑
相關文章
相關標籤/搜索