MySQL核心知識學習之路(7)

作爲一個後端工程師,想必沒有人沒用過數據庫,跟我一起復習一下MySQL吧,本文是我學習《MySQL實戰45講》的總結筆記的第七篇,總結了MySQL是如何解決幻讀的。

上一篇:MySQL核心知識學習之路(6)

1 關於幻讀

我們都知道MySQL的默認隔離級別是可重複讀(點此複習MySQL的事務隔離),它仍然存在一個問題:幻讀。

啥是幻讀?

幻讀指在同一個事務中,存在前後兩次查詢同一個範圍的數據,但是第二次查詢卻看到了第一次查詢沒看到的行。

啥時候會出現幻讀?

事務的隔離級別爲可重複讀,並且是當前讀。

select * from t where id =1; -- 屬於快照讀。
select * from t where id =1 for update; -- 屬於當前讀。

一般情況下,幻讀僅指新插入的行。

爲何會出現幻讀?

因爲行鎖只能鎖定存在的行,針對新插入的操作沒有限定。

幻讀會帶來啥問題?

對行鎖語義的破壞 和 破壞了數據一致性。

如何解決幻讀呢?

最簡單的方法就是:升級事務隔離級別到可串行化,但這會讓MySQL失去併發處理能力。

加之現有的行鎖也解決不了幻讀,因爲即使鎖住所有記錄,還是阻止不了插入新數據。

所以,MySQL InnoDB引擎創造了一種新的鎖,目的是鎖住記錄之間的“間隙”,且看下一部分的介紹。

2 間隙鎖(gap lock)

啥是間隙鎖?

顧名思義,間隙鎖,鎖的就是兩個值之間的空隙

比如下圖所示的某張表t,在該表主鍵索引(id)上插入了6個記錄(0,5,10,15,20,25),因此產生了7個間隙。

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

圖片來源:林曉斌《MySQL實戰45講》

間隙鎖是專門用於解決幻讀這種問題的鎖,它鎖了行與行之間的間隙,這樣就能夠阻塞新插入的操作

因此,劃重點:當MySQL InnoDB引擎在一行行掃描的過程中,不僅會給行加上行鎖,還會給行的兩邊的空隙也加上間隙鎖

補充:間隙鎖之間是不衝突的,跟間隙鎖存在衝突關係的只是“往這個間隙中插入一個記錄”的操作。

注意事項

間隙鎖在可重複讀級別下才是有效的(換句話說,如果調整隔離級別爲讀提交就沒有間隙鎖了)。

間隙鎖的引入也帶來了一些新的問題,比如:降低併發度,可能導致死鎖。

3 next-key lock

由於間隙鎖(gap lock)仍在存在一些問題,可能會降低併發度和仍然可能導致死鎖。因此,MySQL InnoDB爲間隙鎖引入了一個補充:next-key lock。

那麼,問題來了:啥是next-key lock?

所謂next-key lock,它是間隙鎖和行鎖的合體,每個next-key lock都是前開後閉區間,如 (0,5]。

間隙鎖都是開區間,如 (0,5)。

next-key lock幫助MySQL在默認隔離級別下解決了幻讀問題,因此它也是MySQL加鎖的基本單位。

MySQL加鎖的原則

(1)加鎖的基本單位是 next-key lock

(2)查找過程中訪問到的對象纔會加鎖

該原則適用的前提條件爲:

MySQL版本:5.x系列<=5.7.24,8.0系列<=8.0.13,且只有在可重複讀隔離級別下(切換到讀提交的話就只剩下行鎖了)。

MySQL加鎖的優化

索引上的等值查詢,如果是給唯一索引加鎖,此時next-key lock 退化爲行鎖。

索引上的等值查詢,如果不是唯一索引,需要向右遍歷訪問到第一個值不滿足等值條件的時候,此時next-key lock 退化爲間隙鎖。

MySQL加鎖的Bug

唯一索引上的範圍查詢會訪問到第一個不滿足條件的值爲止。

4 小結

本文總結了MySQL的InnoDB引擎是如何解決幻讀問題的,即通過 間隙鎖 + 行鎖組成的next-key lock來實現的。

參考資料

林曉斌(丁奇),《MySQL實戰45講》

👇掃碼訂閱《MySQL實戰45講》

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章