簡介:
大家好,我是xp。
突然想起,上篇的MySQL5.7 MVCC原理分析與調(diào)試只是描述了mvcc解決不可重復讀的情況,并沒有描述如何解決幻讀的。
幻讀:側(cè)重于insert、delete這種操作,第一次查出生成視圖之后,即便有別的事務insert、delete,也不影響后續(xù)的查詢。
首先解釋下本次涉及的3種鎖:
- X鎖:俗稱寫鎖/排他鎖,即加鎖之后,不允許別的事務來修改當前數(shù)據(jù)。
- GAP鎖:俗稱間隙鎖,就是鎖住某個范圍(RR級別解決幻讀的關鍵)
- Next-Key鎖:GAP鎖+記錄本身
著重強調(diào)一下,所有鎖,都是針對索引的,鎖的是索引,記住。如果沒有索引,那就芭比Q了。
八股文:
B+tree:非葉子節(jié)點只存儲索引,所有葉子節(jié)點之間都有一個鏈指針,不存儲數(shù)據(jù),數(shù)據(jù)記錄都存放在葉子節(jié)點中。
如下圖所示:
上面是B+tree的數(shù)據(jù)結(jié)構,一定要記住葉子節(jié)點,里面包含了索引和data(主鍵/其他數(shù)據(jù))。
有一點需要說明:葉子節(jié)點是按頁存儲的,頁之間是雙向鏈表,頁里面的葉子節(jié)點是單向鏈表。
來個預熱:
場景:
1:表結(jié)構(id是主鍵,count為普通索引):
開啟事務1:
先查詢count = 5,生成視圖。
開始事務2:
此時插入一條count = 4的數(shù)據(jù),很明顯,被阻塞住了。
為啥要阻塞count = 4的記錄呢?因為RR級別下,其實是加了GAP鎖,不允許插入某個范圍的值,從而避免了幻讀的出現(xiàn),那么這個范圍是怎么定義的呢?
這是官網(wǎng)的,應用于我上面的例子,鎖住的就是(3,5],(5,7],左開右閉。
好了,大家都散了吧。結(jié)論已經(jīng)得出了:
加了范圍鎖,不允許插入數(shù)據(jù),所以不會出現(xiàn)幻讀的情況,范圍如上。
什么,你反對?
對于GAP的范圍,看到網(wǎng)上很多討論,有的說是測試的左開右閉,也有說測試的左閉右開。
這里,我想說,雖然實踐是檢驗真理的標準,但是實踐的前提是,你得明白實踐方式對不對。
好,我們繼續(xù),上面的測試情況:
可以具體成上圖,因為是普通索引,所以data只包含了主鍵。
那么鎖住的是哪一塊呢?
鎖的其實是上述的區(qū)間,而左開右閉,左閉右開都是錯誤的。要看插入的位置
超出那個區(qū)間的就可以插入,反之即不可以插入,這個位置是按主鍵排序的。
- count=3,id=32,可以插入
- count=3,id=34,不可以插入
- count=7,id=76,不可以插入
- count=7,id=78,可以插入
我們可以驗證一下:
1.count=3,id=32:
插入成功,沒有阻塞。
2.count=3,id=34:
插入失敗,阻塞。
3.count=7,id=76:
插入失敗,阻塞。
4.count=7,id=78:
插入成功,沒有阻塞。
驗證GAP:
這下是真的結(jié)束了,大家可以散了。
什么?還沒走?那么來傳授一個面試裝13技巧:
如果是更新語句,盡量以主鍵為條件,唯一約束也可以,因為主鍵和唯一約束都只會把更新的那一行加X鎖。再不濟,也要是以普通索引去更新。
大忌:以非索引字段去更新,這樣會導致所有的列都加X鎖,此時所有的 insert/update 都無法操作。
驗證(count已經(jīng)改為非索引):
可以看到,阻塞住了。
查詢INNODB_LOCKS:
supremum pseudo-record:表示無窮大