天天看點

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

1. 結論

首先說結論,在RR的隔離級别下,Innodb使用MVCC和next-key locks解決幻讀,MVCC解決的是普通讀(快照讀)的幻讀,next-key locks解決的是目前讀情況下的幻讀。

2. 幻讀是什麼

事務A,先執行:

update table set name=“hh” where id>3;      

結果為:

OK row xx 表名成功影響多少行資料

事務B,後執行,并且送出:

insert into table values(11, uu);

commit;      

事務A,然後再select一下:

select * from table where id>3      

結果集為:

… 11,uu …

事務A懵了,我特麼不是id>3全部更新了嗎

這次是已送出事務B對事務A産生的影響,這個影響叫做“幻讀”。

幻讀和不可重複讀的差別是,前者是一個範圍,後者是本身

3. 怎麼解決的?

3.1. 目前讀

所謂目前讀,指的是加鎖的select(S或者X), update, delete等語句。在RR的事務隔離級别下,資料庫會使用next-key locks來鎖住本條記錄以及索引區間。

拿上面那個例子來說,在RR的情況下,假設使用的是目前讀,加鎖了的讀

select * from table where id>3 鎖住的就是id=3這條記錄以及id>3這個區間範圍,鎖住索引記錄之間的範圍,避免範圍間插入記錄,以避免産生幻影行記錄。

搜尋Java知音公衆号,回複“後端面試”,送你一份Java面試題寶典.pdf

3.2. 普通讀

因為普通讀是不會加鎖的讀,故不會有next-key locks的使用,解決幻讀的手段是MVCC

MVCC會給每行元組加一些輔助字段,記錄建立版本号和删除版本号。

而每一個事務在啟動的時候,都有一個唯一的遞增的版本号。每開啟一個新事務,事務的版本号就會遞增。

預設的隔離級别(REPEATABLE READ)下,增删查改變成了這樣:

SELECT

  • 讀取建立版本小于或等于目前事務版本号,并且删除版本為空或大于目前事務版本号的記錄。這樣可以保證在讀取之前記錄是存在的

INSERT

  • 将目前事務的版本号儲存至行的建立版本号

UPDATE

  • 新插入一行,并以目前事務的版本号作為新行的建立版本号,同時将原記錄行的删除版本号設定為目前事務版本号

DELETE

  • 将目前事務的版本号儲存至行的删除版本号

比如我插入一條記錄, 事務id 假設是1 ,那麼記錄如下:也就是說,建立版本号就是事務版本号。

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

如果我更新的話,事務id假設是2

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

這裡是把name更新為taotao,原來的元組deleteversion版本号為這個事務的id,并且新增一條

如果我删除的話,假設事務是id=3        

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

就變成現在這個樣子

關鍵點來了

現在我讀取的話,必須同時滿足兩個條件的

  • 讀取建立版本小于或等于目前事務版本号   這意味着資料在這個事務之前被建立
  • 删除版本為空或大于目前事務版本号的記錄。 這意味着删除操作在這個事務之後發生

就拿上面那個例子說明

目前資料庫的狀态

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

假設事務A的id=10

現在update table set name=“hh” where id>3;執行這條語句

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

事務B的id=11

insert into table values(11, uu);

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

最後事務A(id=10)在此讀取

select * from table where id>3      

根據上述的規則,讀取建立版本好小于等于目前事務的→那麼(4,a)(5,b)(4,hh)(5,hh)

上面規則的輸出作為下面規則的輸入的話,删除版本為空或大于目前事務版本号的記錄→(4,hh)(5,hh)

如此讀取就沒有讀取到事務B新插入的那行,解決幻讀

如果事務B是更新id=4 的元組name=cc呢

同理,根據update的規則

面試官:你說熟悉MySQL,那來談談InnoDB怎麼解決幻讀的?

然後根據select的規則去讀取的話,得到的還是(4,hh)(5,hh)

4. 多說一句

在RC的模式下,MVCC解決不了幻讀和不可重複讀,因為每次讀都會讀它自己重新整理的快照版本,簡單來說就是另一個事務送出,他就重新整理一次,去讀最新的

繼續閱讀