MySQL的加鎖規則


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

首先是課程中的總結的加鎖規則,兩個“原則”、兩個“優化”和一個“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);

再然后就是實際例子了

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

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

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

 這個和上一個例子類似區別是滿足優化1所以就從next-key lock鎖id(5,10],退化為id=5的行鎖,所以id=6,4的數據可以插入進去,只有id=5的數據在更新的時候等待鎖。

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=4、11的數據可以插入,而id=6、10的數據的更新和插入的時候需要等待鎖

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

 分析:

  • 根據原則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鎖

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=5、c=10、c=15都有值,且select id,所以這三行的id索引上加了3個行鎖
  • 所以最終情況是c(5,20),id=5、10、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來判斷怎么加間隙鎖

 


免責聲明!

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



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