MySQL中的InnoDB是怎么解決幻讀的?
點擊上方藍色“程序猿DD”,選擇“設為星標”
回復“資源”獲取獨家整理的學習資料!

作者 |?Aaron_濤
1. 結論
首先說結論,在RR的隔離級別下,Innodb使用MVCC和next-key locks解決幻讀,MVCC解決的是普通讀(快照讀)的幻讀,next-key locks解決的是當前讀情況下的幻讀。
2. 幻讀是什么
事務A,先執(zhí)行:
update?table?set?name=“hh”?where?id>3;
結果為:
OK row xx 表名成功影響多少行數(shù)據(jù)
事務B,后執(zhí)行,并且提交:
insert?into?table?values(11,?uu);
commit;
事務A,然后再select一下:
select?*?from?table?where?id>3
結果集為:
… 11,uu …
事務A懵了,我特么不是id>3全部更新了嗎
這次是已提交事務B對事務A產生的影響,這個影響叫做“幻讀”。
幻讀和不可重復讀的區(qū)別是,前者是一個范圍,后者是本身
3. 怎么解決的?
3.1. 當前讀
所謂當前讀,指的是加鎖的select(S或者X), update, delete等語句。在RR的事務隔離級別下,數(shù)據(jù)庫會使用next-key locks來鎖住本條記錄以及索引區(qū)間。
拿上面那個例子來說,在RR的情況下,假設使用的是當前讀,加鎖了的讀
select * from table where id>3?鎖住的就是id=3這條記錄以及id>3這個區(qū)間范圍,鎖住索引記錄之間的范圍,避免范圍間插入記錄,以避免產生幻影行記錄。
3.2. 普通讀
因為普通讀是不會加鎖的讀,故不會有next-key locks的使用,解決幻讀的手段是MVCC
MVCC會給每行元組加一些輔助字段,記錄創(chuàng)建版本號和刪除版本號。
而每一個事務在啟動的時候,都有一個唯一的遞增的版本號。每開啟一個新事務,事務的版本號就會遞增。
默認的隔離級別(REPEATABLE READ)下,增刪查改變成了這樣:
SELECT
讀取創(chuàng)建版本小于或等于當前事務版本號,并且刪除版本為空或大于當前事務版本號的記錄。這樣可以保證在讀取之前記錄是存在的
INSERT
將當前事務的版本號保存至行的創(chuàng)建版本號
UPDATE
新插入一行,并以當前事務的版本號作為新行的創(chuàng)建版本號,同時將原記錄行的刪除版本號設置為當前事務版本號
DELETE
將當前事務的版本號保存至行的刪除版本號
比如我插入一條記錄, 事務id 假設是1 ,那么記錄如下:也就是說,創(chuàng)建版本號就是事務版本號。

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

這里是把name更新為taotao,原來的元組deleteversion版本號為這個事務的id,并且新增一條
如果我刪除的話,假設事務是id=3? ? ? ??

? ? ?
就變成現(xiàn)在這個樣子
關鍵點來了
現(xiàn)在我讀取的話,必須同時滿足兩個條件的
讀取創(chuàng)建版本小于或等于當前事務版本號 ? 這意味著數(shù)據(jù)在這個事務之前被創(chuàng)建 刪除版本為空或大于當前事務版本號的記錄。?這意味著刪除操作在這個事務之后發(fā)生
就拿上面那個例子說明
當前數(shù)據(jù)庫的狀態(tài)

假設事務A的id=10
現(xiàn)在update table set name=“hh” where id>3;執(zhí)行這條語句

事務B的id=11
insert into table values(11, uu);

最后事務A(id=10)在此讀取
select?*?from?table?where?id>3
根據(jù)上述的規(guī)則,讀取創(chuàng)建版本好小于等于當前事務的→那么(4,a)(5,b)(4,hh)(5,hh)
上面規(guī)則的輸出作為下面規(guī)則的輸入的話,刪除版本為空或大于當前事務版本號的記錄→(4,hh)(5,hh)
如此讀取就沒有讀取到事務B新插入的那行,解決幻讀
如果事務B是更新id=4 的元組name=cc呢
同理,根據(jù)update的規(guī)則

然后根據(jù)select的規(guī)則去讀取的話,得到的還是(4,hh)(5,hh)
4. 多說一句
在RC的模式下,MVCC解決不了幻讀和不可重復讀,因為每次讀都會讀它自己刷新的快照版本,簡單來說就是另一個事務提交,他就刷新一次,去讀最新的
其他資料
https://segmentfault.com/a/1190000012669504
往期推薦


我的星球是否適合你?
點擊閱讀原文看看我們都聊過啥?
