首先眾所周知,InnoDB 三種行鎖:
Record Lock(記錄鎖) :鎖住某一行記錄
Gap Lock(間隙鎖) :鎖住一段左開右開的區間
Next-key Lock(臨鍵鎖) :鎖住一段左開右閉的區間
哪些語句上面會加行鎖?
1)對于常見的 DML 語句(如 UPDATE、DELETE 和 INSERT ),InnoDB 會自動給相應的記錄行加寫鎖
2)默認情況下對于普通 SELECT 語句,InnoDB 不會加任何鎖,但是在 Serializable 隔離級別下會加行級讀鎖
上面兩種是隱式鎖定,InnoDB 也支持通過特定的語句進行顯式鎖定:
3)SELECT * FROM table_name WHERE ... FOR UPDATE,加行級寫鎖
4)SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE,加行級讀鎖
前置知識就不過多介紹了,在學習具體行鎖加鎖規則之前,小伙伴們需要記住加鎖規則的兩條核心:
1)查找過程中訪問到的對象才會加鎖
這句話該怎么理解?比如有主鍵 id 為 1 2 3 4 5 ... 10 的 10 條記錄,我們要找到 id = 7 的記錄。注意,查找并不是從第一行開始一行一行地進行遍歷,而是根據 B+ 樹的特性進行二分查找,所以一般存儲引擎只會訪問到要找的記錄行(id = 7)的相鄰區間
2)加鎖的基本單位是 Next-key Lock
下面結合實例幫助大伙分析一條 SQL 語句上面究竟被 InnoDB 自動加上了多少個鎖
假設有這么一張 user 表,id 為主鍵(唯一索引),a 是普通索引(非唯一索引),b都是普通的列,其上沒有任何索引:
id (唯一索引) | a (非唯一索引) | b |
---|---|---|
10 | 4 | Alice |
15 | 8 | Bob |
20 | 16 | Cilly |
25 | 32 | Druid |
30 | 64 | Erik |
案例 1:唯一索引等值查詢
當我們用唯一索引進行等值查詢的時候,根據查詢的記錄是否存在,加鎖的規則會有所不同:
當查詢的記錄是存在的,Next-key Lock 會退化成記錄鎖
當查詢的記錄是不存在的,Next-key Lock 會退化成間隙鎖
查詢的記錄存在
先來看個查詢的記錄存在的案例:
select*fromuser whereid=25 forupdate;
結合加鎖的兩條核心:查找過程中訪問到的對象才會加鎖 + 加鎖的基本單位是 Next-key Lock(左開右閉),我們可以分析出,這條語句的加鎖范圍是 (20, 25]
不過,由于這個唯一索引等值查詢的記錄 id = 25 是存在的,因此,Next-key Lock 會退化成記錄鎖,因此最終的加鎖范圍是 id = 25 這一行
查詢的記錄不存在
再來看查詢的記錄不存在的案例:
select*fromuser whereid=22 forupdate;
結合加鎖的兩條核心:查找過程中訪問到的對象才會加鎖 + 加鎖的基本單位是 Next-key Lock(左開右閉),我們可以分析出,這條語句的加鎖范圍是 (20, 25]
這里為什么是 (20,25] 而不是 (20, 22],因為 id = 22 的記錄不存在呀,InnoDB 先找到 id = 20 的記錄,發現不匹配,于是繼續往下找,發現 id = 25,因此,id = 25 的這一行被掃描到了,所以整體的加鎖范圍是 (20, 25]
由于這個唯一索引等值查詢的記錄 id = 22 是不存在的,因此,Next-key Lock 會退化成間隙鎖,因此最終在主鍵 id 上的加鎖范圍是 Gap Lock (20, 25)
案例 2:唯一索引范圍查詢
唯一索引范圍查詢的規則和等值查詢的規則一樣,只有一個區別,就是唯一索引的范圍查詢需要一直向右遍歷到第一個不滿足條件的記錄,下面結合案例來分析:
select*fromuser whereid>=20andid22 for?update;
先來看語句查詢條件的前半部分 id >= 20,因此,這條語句最開始要找的第一行是 id = 20,結合加鎖的兩個核心,需要加上 Next-key Lock (15,20]。又由于 id 是唯一索引,且 id = 20 的這行記錄是存在的,因此會退化成記錄鎖,也就是只會對 id = 20 這一行加鎖。
再來看語句查詢條件的后半部分 id < 22,由于是范圍查找,就會繼續往后找第一個不滿足條件的記錄,也就是會找到 id = 25 這一行停下來,然后加 Next-key Lock (20, 25],重點來了,但由于 id = 25 不滿足 id < 22,因此會退化成間隙鎖,加鎖范圍變為 (20, 25)。
所以,上述語句在主鍵 id 上的最終的加鎖范圍是 Record Lock id = 20 以及 Gap Lock (20, 25)
案例 3:非唯一索引等值查詢
當我們用非唯一索引進行等值查詢的時候,根據查詢的記錄是否存在,加鎖的規則會有所不同:
1、當查詢的記錄是存在的,除了會加 Next-key Lock 外,還會額外加間隙鎖(規則是向下遍歷到第一個不符合條件的值才能停止),也就是會加兩把鎖
很好記憶,就是要查找記錄的左區間加 Next-key Lock,右區間加 Gap lock
2、當查詢的記錄是不存在的,Next-key Lock 會退化成間隙鎖(這個規則和唯一索引的等值查詢是一樣的)
查詢的記錄存在
先來看個查詢的記錄存在的案例:
select*fromuser wherea=16 forupdate;
結合加鎖的兩條核心,這條語句首先會對普通索引 a 加上 Next-key Lock,范圍是 (8,16]
又因為是非唯一索引等值查詢,且查詢的記錄 a= 16 是存在的,所以還會加上間隙鎖,規則是向下遍歷到第一個不符合條件的值才能停止,因此間隙鎖的范圍是 (16,32)
所以,上述語句在普通索引 a 上的最終加鎖范圍是 Next-key Lock (8,16] 以及 Gap Lock (16,32)
查詢的記錄不存在
再來看查詢的記錄不存在的案例:
select*fromuser wherea=18 forupdate;
結合加鎖的兩條核心,這條語句首先會對普通索引 a 加上 Next-key Lock,范圍是 (16,32]
但是由于查詢的記錄 a = 18 是不存在的,因此 Next-key Lock 會退化為間隙鎖,即最終在普通索引 a 上的加鎖范圍是 (16,32)。
案例 4:非唯一索引范圍查詢
范圍查詢和等值查詢的區別在上面唯一索引章節已經介紹過了,就是范圍查詢需要一直向右遍歷到第一個不滿足條件的記錄,和唯一索引范圍查詢不同的是,非唯一索引的范圍查詢并不會退化成 Record Lock 或者 Gap Lock。
select*fromuser wherea>=16anda18 for?update;
先來看語句查詢條件的前半部分 a >= 16,因此,這條語句最開始要找的第一行是 a = 16,結合加鎖的兩個核心,需要加上 Next-key Lock (8,16]。雖然非唯一索引 a = 16的這行記錄是存在的,但此時并不會像唯一索引那樣退化成記錄鎖。
再來看語句查詢條件的后半部分 a < 18,由于是范圍查找,就會繼續往后找第一個不滿足條件的記錄,也就是會找到 id = 32 這一行停下來,然后加 Next-key Lock (16, 32]。雖然 id = 32 不滿足 id < 18,但此時并不會向唯一索引那樣退化成間隙鎖。
所以,上述語句在普通索引 a 上的最終的加鎖范圍是 Next-key Lock (8, 16] 和 (16, 32],也就是 (8, 32]。
審核編輯:劉清
-
SQL
+關注
關注
1文章
760瀏覽量
44076 -
GAP
+關注
關注
0文章
15瀏覽量
8298 -
DML模型
+關注
關注
0文章
4瀏覽量
6011
原文標題:美團:這個 SQL 語句加了哪些鎖?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論