你瞭解MySQL的加鎖規則嗎?

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

加鎖規則能夠歸納爲:兩個原則、兩個優化和一個bug:
原則1:加鎖的基本單位是next-key lock,前開後閉
原則2:查找過程當中訪問到的對象纔會加鎖
優化1:索引上的等值查詢,給惟一索引加鎖的時候,next-key lock退化成行鎖
優化2:索引上的等值查詢,向右遍歷時且最後一個值不知足等值條件的時候,next-key lock退化爲間隙鎖
1個bug:惟一索引上的範圍查詢會訪問到不知足條件的第一個值爲止。
——丁奇

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

測試數據:
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是分爲間隙鎖和行鎖兩端來執行的。
相關文章
相關標籤/搜索