你了解MySQL的加鎖規則嗎?


注:加鎖規則指的是next-key lock,如果還不了解next-key lock,請閱讀上一篇博客

加鎖規則可以概括為:兩個原則、兩個優化和一個bug:
原則1:加鎖的基本單位是next-key lock,前開后閉
原則2:查找過程中訪問到的對象才會加鎖
優化1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock退化成行鎖
優化2:索引上的等值查詢,向右遍歷時且最后一個值不滿足等值條件的時候,next-key lock退化為間隙鎖
1個bug:唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。
——丁奇

這里用丁奇老師總結的規則,分場景進行分析:

測試數據:
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB;

insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);
接下來來看幾個案例:

案例一:等值查詢間隙鎖

 
  1. 由於數據庫中沒有 id=7這條數據,id又為主鍵索引,所以根據原則1可得:next-key lock的加鎖范圍是(5,10]。
  2. SessionB要往這個間隙中插入id=8的數據,會被鎖住,而SessionA是一個等值查詢(id=7),且SessionCid=10的查詢不滿足查詢條件(7 != 10),根據優化2可得,此時next-key lock退化成間隙鎖:(5,10)
因此,SessionB會被阻塞,SessionC不會被阻塞

案例二:非唯一索引等值鎖

  1. 根據原則1,加鎖的單位是next-key lock,因此會(0,5]加上next-key lock
  2. c是普通索引,所以找到c=5這一條數據后,並不會停下,而是繼續向右遍歷,直到c=10,根據原則2,訪問到的都要進行加鎖,所以要給(5,10]加上next-key lock
  3. 由於優化2,等值判斷,向右遍歷,最后一個值不滿足c=5這個等值條件,所以next-key lock退化成間隙鎖(5,10)
  4. 根據原則2,訪問到的數據才會加鎖,又因為SessionB這個查詢使用了覆蓋索引,只需要訪問主鍵索引,而主鍵索引並沒有加鎖,所以SessionB可以執行。(鎖是加在索引上的)SessionC要插入的元素位於(5,10)這個間隙鎖的范圍內,所以會被阻塞。

案例三:主鍵索引范圍鎖

  1. SessionA的語句執行時,由於id是主鍵索引,找到了第一行id=10的數據,所以此時的next-key lock應該為(5,10],根據優化1,主鍵id的等值條件,退化成行鎖。
  2. 往后繼續查找,到id=15這一行停下來,因此需要加next-key lock(10,15]
  3. 所以此時這個next-key lock的范圍就是[10,15]

案例四:非唯一索引范圍鎖

 
類似於案例3,唯一不同的地方是,在第一次使用c=10定位記錄的時候,索引c上加上了(5,10]的next-key lock,這個next-key lock不會退化,所以最終SessionA加的鎖是,索引c上的(5,10]和(10,15]這兩個next-key lock

案例五:唯一索引范圍鎖bug

正常來說,唯一索引的next-key lock應該是(10,15],但是由於bug1,掃描到15后,還會繼續掃描第一個不符合條件的值,因此還會存在一個next-key lock范圍是(15,20],這應該算是一個bug,因為(10,15]的next-key lock已經可以保證不會有范圍內的數據插入。

案例六:limit語句加鎖:

這兩條語句的執行結果是一樣的, 但是加鎖范圍卻不一樣。
c是普通索引,所以SessionA加的next-key lock的范圍是(5,10]和(10,15)
但是由於這里的limit已經明確了數據量的限制,因此在遍歷到(c=10,id=30)這一行數據的時候,滿足條件的語句已經有2條了。所以next-key lock的范圍就變成了從(c=5,id=5)到(c=10,id=30)
通過這個案例,我們可以得到一個結論, 在刪除數據的時候,盡量指定limit,這樣不僅可以控制刪除數據的條數,讓操作更安全,也可以減少加鎖的范圍

案例七:死鎖

  1. sessionA啟動事務后,執行查詢語句加lock in share mode,在索引C上加上了next-key lock(5,10]和間隙鎖(10,15)
  2. SessionB的更新語句也要在c上加next-key lock(5,10]進入等待。
  3. SessionA要再插入 (8,8,8)這一行,被SessionB的間隙鎖鎖住,出現了死鎖。InnoDB讓SessionB回滾。
這里就有一個問題,SessionB的 next-key lock(5,10]不是沒申請成功嗎?為什么還會阻塞SessionA?
其實SessionB的加next-key lock(5,10]是分成 兩步操作的,首先加(5,10)的間隙鎖,在加c=10的行鎖,間隙鎖不是排他鎖,所以加鎖成功。也就是說next-key lock是分為間隙鎖和行鎖兩端來執行的。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM