MySQL的加鎖規則

此次的內容是學習極客時間的MySQL實戰45講課程中的實驗和總結,具體課程是第21篇文章。sql

首先是課程中的總結的加鎖規則,兩個「原則」、兩個「優化」和一個「bug」(可重複讀的事務隔離級別下)。學習

原則 1:加鎖的基本單位是 next-key lock。但願你還記得,next-key lock 是前開後閉區間。

原則 2:查找過程當中訪問到的對象纔會加鎖。

優化 1:索引上的等值查詢,給惟一索引加鎖的時候,next-key lock 退化爲行鎖。

優化 2:索引上的等值查詢,向右遍歷時且最後一個值不知足等值條件的時候,next-key lock 退化爲間隙鎖。

一個 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);

再而後就是實際例子了3d

1.在主鍵上,訪問不存在的數據

根據原則1加的next-key lock鎖是id(5,10],由於id等於7的數據並不存在,因此不知足優化1,根據優化2將會退化爲間隙鎖id(5,10),從上面的實際例子中也能看出來,只有插入id=6的數據在等待鎖,id=四、十一、十、5的數據在插入和更新的時候都正常。對象

2.在主鍵上,訪問存在的數據

 

 

 這個和上一個例子相似區別是知足優化1因此就從next-key lock鎖id(5,10],退化爲id=5的行鎖,因此id=6,4的數據能夠插入進去,只有id=5的數據在更新的時候等待鎖。blog

3.在主鍵上 範圍查詢時會根據掃描的狀況加鎖

 

 

 這種狀況就是索引

  • 根據原則1加的是next-key lock鎖id(0,5],
  • 根據bug知道掃描到了10因此還加了next-key lock鎖id(0,10]
  • 再而後根據優化2,由於這裏有等值查詢(id=5),因此next-key lock鎖id(0,5]退化成了id=5行鎖

因此id=四、11的數據能夠插入,而id=六、10的數據的更新和插入的時候須要等待鎖事務

4.在主鍵上 範圍查詢時會根據掃描的狀況加鎖2

 

 

分析:it

  • 根據原則1加上了id(5,10],id(10,15]的next-key lock鎖
  • 根據優化1id(10,15]的next-key lock鎖退化成行鎖id=10

可是實際狀況上id=11的數據插入也須要等待鎖,這就是上面規則說的bug惟一索引上的範圍查詢會訪問到不知足條件的第一個值爲止,這裏的id<=10,會一直掃描到id=15,因此會加上(10,15]的next-key lock鎖class

5.在非惟一索引上等值查詢的狀況

這實際上是兩個例子,由於只有一個區別就放一塊兒了,先按照規則來分析

  • 根據原則1就能知道都加上了c(0,5],c(5,10]的next-key lock鎖
  • 根據優化2c(5,10]的next-key lock鎖會退化爲c(5,10)的間隙鎖

這兩個加鎖的區別就是對於id=5的鎖的問題,lock in share mode沒有對id=5加鎖,因此能夠獲得的信息是,lock in share mode只鎖覆蓋索引,可是若是是 for update 就不同了。 執行 for update 時,系統會認爲你接下來要更新數據,所以會順便給主鍵索引上知足條件的行加上行鎖。

6.在非惟一索引上範圍查詢的狀況

 

 

 這個和3例子相似,區別是這裏不是主鍵,也就是說不是惟一索引,因此這裏的分析過程就是

  • 根據原則1加上了c(0,5],c(5,10]的next-key lock鎖
  • 由於不是惟一索引不知足優化1,因此c(0,5]不會退化成c=5的行鎖,因此最終結果就是c(0,5],c(5,10]

7.在非惟一索引範圍查詢時 會根據掃描的狀況加鎖

 

 

 這個例子和第5個例子相似,區別就是limit的問題,由於規則中總結的bug能夠解釋,limit 1只須要掃描一行,因此c(5,10)的間隙鎖就沒加上,而limit 2就再次證實了,若是須要返回兩條數據須要再掃描就加上了c(5,10)的間隙鎖

 

 

 沒有order by的狀況先來分析:

  • 根據原則1c(5,10]、c(10,15]、c(15,20]都加上了next-key lock鎖
  • 根據優化2c(15,20]退化爲間隙鎖,c(15,20)
  • 根據bugc<=15會一直掃描到20,因此c(10,15]的鎖仍是會加上
  • 因此最終結果是c(5,10]、c(10,15]、c(15,20]都加上了鎖

而後是有order by desc的狀況:

  • 由於order by因此第一個定位的是索引上c=15的數據行,因此會加上c(10,15]的next-key lock鎖和c(15,20)的間隙鎖
  • 由於向左掃描,掃描到了c=5才停下來,因此加上了c(0,5]、c(5,10]的next-key lock鎖
  • 由於c=五、c=十、c=15都有值,且select id,因此這三行的id索引上加了3個行鎖
  • 因此最終狀況是c(5,20),id=五、十、15的鎖

總結

這上面總結的規則,其實沒啥道理可言,MySQL代碼就是這樣寫的,加鎖的意義是實際上爲了保持數據的一致性和語義的正確,從例子4咱們就能看到明明咱們想鎖的是5-10,可是由於bug(10,15]也加上了鎖。因此考慮MySQL的這個加鎖問題的時候,最好仍是根據掃描的狀況來考慮

而後是一些細節的記錄:

  • <=a 怎麼判斷是間隙鎖仍是行鎖,這個要看掃描的過程來看,要先找到這個a,這個用等值判斷來斷定,而後在索引中掃描的過程使用範圍查找來判斷,就像最後一個例子中的order by desc同樣,向左向右掃描出現的狀況也不同
  • 加鎖的狀況其實不單單是經過for update 和lock in share mode來決定的,還得看查詢的結果,若是是select * 查到了主鍵,同樣會鎖主鍵索引,這和訪問的對象有關,訪問到了就加鎖,就像掃描同樣
  • 有行纔會加行鎖,若是查詢沒有命中行就加next-key lock鎖,而後若是是等值查詢還須要根據優化2來判斷怎麼加間隙鎖
相關文章
相關標籤/搜索