【 58沈劍 架構師之路】InnoDB七種鎖——記錄鎖、間隙鎖、臨鍵鎖

MySQL的InnoDB的細粒度行鎖,是它最吸引人的特性之一。session

 

可是,如《InnoDB,5項最佳實踐》所述,若是查詢沒有命中索引,也將退化爲表鎖。併發

 

InnoDB的細粒度鎖,是實如今索引記錄上的。spa

 

一,InnoDB的索引3d

InnoDB的索引有兩類索引,彙集索引(Clustered Index)與普通索引(Secondary Index)。索引

 

InnoDB的每個表都會有彙集索引事務

(1)若是表定義了PK,則PK就是彙集索引;get

(2)若是表沒有定義PK,則第一個非空unique列是彙集索引;it

(3)不然,InnoDB會建立一個隱藏的row-id做爲彙集索引;io

爲了方便說明,後文都將以PK說明。date

 

索引的結構是B+樹,這裏不展開B+樹的細節,說幾個結論:

(1)在索引結構中,非葉子節點存儲key,葉子節點存儲value;

(2)彙集索引葉子節點存儲行記錄(row)

畫外音:因此,InnoDB索引和記錄是存儲在一塊兒的,而MyISAM的索引和記錄是分開存儲的。

 

(3)普通索引葉子節點存儲了PK的值

畫外音:

因此,InnoDB的普通索引,實際上會掃描兩遍:

第一遍,由普通索引找到PK;

第二遍,由PK找到行記錄;

索引結構,InnoDB/MyISAM的索引結構,若是你們感興趣,將來撰文詳述。

 

舉個例子,假設有InnoDB表:

t(id PK, name KEY, sex, flag);

 

表中有四條記錄:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

 

以看到:

(1)第一幅圖,id PK的彙集索引,葉子存儲了全部的行記錄;

(2)第二幅圖,name上的普通索引,葉子存儲了PK的值;

 

對於:

select * from t where name=’shenjian’;

(1)會先在name普通索引上查詢到PK=1;

(2)再在彙集索引衫查詢到(1,shenjian, m, A)的行記錄;

 

下文簡單介紹InnoDB七種鎖中的剩下三種:

  • 記錄鎖(Record Locks)

  • 間隙鎖(Gap Locks)

  • 臨鍵鎖(Next-Key Locks)

爲了方便講述,如無特殊說明,後文中,默認的事務隔離級別爲可重複讀(Repeated Read, RR)。

 

2、記錄鎖(Record Locks)

記錄鎖它封鎖索引記錄,例如:

select * from t where id=1 for update;

 

它會在id=1的索引記錄上加鎖,以阻止其餘事務插入,更新,刪除id=1的這一行。

 

須要說明的是:

select * from t where id=1;

則是快照讀(SnapShot Read),它並不加鎖,具體在《InnoDB爲何併發高,讀取快?》中作了詳細闡述。

 

3、間隙鎖(Gap Locks)

間隙鎖,它封鎖索引記錄中的間隔,或者第一條索引記錄以前的範圍,又或者最後一條索引記錄以後的範圍。

 

依然是上面的例子,InnoDB,RR:

t(id PK, name KEY, sex, flag);

 

表中有四條記錄:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

 

這個SQL語句

select * from t 

    where id between 8 and 15 

    for update;

會封鎖區間,以阻止其餘事務id=10的記錄插入。

畫外音:

爲何要阻止id=10的記錄插入?

若是可以插入成功,頭一個事務執行相同的SQL語句,會發現結果集多出了一條記錄,即幻影數據。

 

間隙鎖的主要目的,就是爲了防止其餘事務在間隔中插入數據,以致使「不可重複讀」。

 

若是把事務的隔離級別降級爲讀提交(Read Committed, RC),間隙鎖則會自動失效。

 

4、臨鍵鎖(Next-Key Locks)

臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖範圍,既包含索引記錄,又包含索引區間。

 

更具體的,臨鍵鎖會封鎖索引記錄自己,以及索引記錄以前的區間。

 

若是一個會話佔有了索引記錄R的共享/排他鎖,其餘會話不能馬上在R以前的區間插入新的索引記錄。

畫外音:原文是說

If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index record in the gap immediately before R in the index order.

 

依然是上面的例子,InnoDB,RR:

t(id PK, name KEY, sex, flag);

 

表中有四條記錄:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

 

PK上潛在的臨鍵鎖爲:

(-infinity, 1]

(1, 3]

(3, 5]

(5, 9]

(9, +infinity]

 

臨鍵鎖的主要目的,也是爲了不幻讀(Phantom Read)。若是把事務的隔離級別降級爲RC,臨鍵鎖則也會失效。

畫外音:關於事務的隔離級別,以及幻讀,以前的文章一直沒有展開說明,若是你們感興趣,後文詳述。

 

今天的內容,主要對InnoDB的索引,以及三種鎖的概念作了介紹。場景與例子,也都是最簡單的場景與最簡單的例子。

 

InnoDB的鎖,與索引類型,事務的隔離級別相關,更多更復雜更有趣的案例,後續和你們介紹。

 

5、總結

(1)InnoDB的索引與行記錄存儲在一塊兒,這一點和MyISAM不同;

(2)InnoDB的彙集索引存儲行記錄,普通索引存儲PK,因此普通索引要查詢兩次

(3)記錄鎖鎖定索引記錄;

(4)間隙鎖鎖定間隔,防止間隔中被其餘事務插入;

(5)臨鍵鎖鎖定索引記錄+間隔,防止幻讀;

相關文章
相關標籤/搜索