之前介紹過,sql標準中,rr級別並不能解決幻讀問題,那麼mysql是如何在rr級別解決幻讀問題的?
鎖協議
在非序列化隔離級別下,普通讀數據是快照讀,寫數據則要加X鎖,並且遵循兩段式鎖協議。也就是申請的鎖會一直佔用,直到事務提交。
幻讀問題
幻讀的定義有兩點需要注意:
1.幻讀只有使用鎖定讀纔可能出現,因爲普通讀使用的是非鎖定讀,會通過MVCC機制獲取到一致性視圖,是看不到新插入的行的;
2.只有讀到新增的數據才叫幻讀;
幻讀的影響:
1.如果只有行鎖的話,語義不正確,沒有鎖住新更新的行;
2.一致性,主庫和從庫的狀態可能不一致;
間隙鎖
上面的X鎖其實是行鎖,即對某一行數據加鎖;這個可以防止髒寫的發生,即鎖住已經存在的行,但是卻無法阻止新插入的行。間隙鎖是專門用來鎖住某一個間隙的,是開區間;
next-key lock
是間隙鎖+行鎖的組合,間隙鎖是行鎖key之前的那個間隙。比如行鎖要鎖住id=5的行,前一個值是id=1的行,那麼該行鎖對應的間隙就是(1,5),next-key lock就是(1,5]。
在rr級別下,默認的加鎖類型就是next-key lock。這樣可以防止新插入的數據導致的幻讀問題。因爲加的是next-key lock,所以鎖定的範圍變大了,相應得,併發性能也會降低,這需要權衡。
在其他級別下加的是行鎖。比如我們在rc級別下,就可以用鎖定讀復現幻讀問題:
事務一:
先執行一條鎖定讀,因爲是在rc級別,只給已經存在的id=1這一行加了行X鎖;
事務二:
也插入了一條gender=1的數據,因爲是新插入的,所以不受事務一加的行鎖阻塞;
事務一:
緊接着,事務一再執行一條鎖定讀,可以看到,讀到了新插入的數據,發生了幻讀;
所以,僅僅在rr級別下才會加next-key lock,在rc級別仍然是行鎖,是會有幻讀問題的;
next-key lock加鎖規則
這個比較複雜,需要結合具體的場景分析了:
訪問到的對象加next-key lock,直到不滿足條件的第一個值,這個值的鎖退化爲間隙鎖;如果是索引上的等值查詢,退化爲行鎖;