當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 小林coding
[導(dǎo)讀]??大家好,我是小林。我之前寫過(guò)一篇數(shù)據(jù)庫(kù)事務(wù)的文章「?事務(wù)、事務(wù)隔離級(jí)別和MVCC」,這篇我說(shuō)過(guò)什么是幻讀。幻讀的定義我這里還得補(bǔ)充一句,幻讀僅專指“新插入的行”,中途通過(guò)update更新數(shù)據(jù)而出現(xiàn)同一個(gè)事務(wù)前后兩次查詢的「結(jié)果集合」不一樣,這種不算幻讀。然后前幾天有位讀者跟我...

?

?大家好,我是小林。

我之前寫過(guò)一篇數(shù)據(jù)庫(kù)事務(wù)的文章「?事務(wù)、事務(wù)隔離級(jí)別和MVCC」,這篇我說(shuō)過(guò)什么是幻讀?;米x的定義我這里還得補(bǔ)充一句,幻讀僅專指“新插入的行”,中途通過(guò) update 更新數(shù)據(jù)而出現(xiàn)同一個(gè)事務(wù)前后兩次查詢的「結(jié)果集合」不一樣,這種不算幻讀。然后前幾天有位讀者跟我說(shuō),這個(gè)幻讀例子不是已經(jīng)被「可重復(fù)讀」隔離級(jí)別解決了嗎?為什么還要有 next-key 呢?他有這個(gè)質(zhì)疑,是因?yàn)樗隽诉@個(gè)實(shí)驗(yàn)。實(shí)驗(yàn)的數(shù)據(jù)庫(kù)表 t_stu 如下,其中 id 為主鍵。
然后在可重復(fù)讀隔離級(jí)別下,有兩個(gè)事務(wù)的執(zhí)行順序如下:
從這個(gè)實(shí)驗(yàn)結(jié)果可以看到,即使事務(wù) B 中途插入了一條記錄,事務(wù) A 前后兩次查詢的結(jié)果集都是一樣的,并沒(méi)有出現(xiàn)所謂的幻讀現(xiàn)象。讀者做的實(shí)驗(yàn)之所以看不到幻讀現(xiàn)象,是因?yàn)樵诳芍貜?fù)讀隔離級(jí)別下,普通的查詢是快照讀,是不會(huì)看到別的事務(wù)插入的數(shù)據(jù)的。可重復(fù)讀隔離級(jí)是由 MVCC(多版本并發(fā)控制)實(shí)現(xiàn)的,實(shí)現(xiàn)的方式是啟動(dòng)事務(wù)后,在執(zhí)行第一個(gè)查詢語(yǔ)句后,會(huì)創(chuàng)建一個(gè)視圖,然后后續(xù)的查詢語(yǔ)句都用這個(gè)視圖,「快照讀」讀的就是這個(gè)視圖的數(shù)據(jù),視圖你可以理解為版本數(shù)據(jù),這樣就使得每次查詢的數(shù)據(jù)都是一樣的。MySQL 里除了普通查詢是快照度,其他都是當(dāng)前讀,比如update、insert、delete,這些語(yǔ)句執(zhí)行前都會(huì)查詢最新版本的數(shù)據(jù),然后再做進(jìn)一步的操作。這很好理解,假設(shè)你要 update 一個(gè)記錄,另一個(gè)事務(wù)已經(jīng) delete 這條記錄并且 提交事務(wù)了,這樣不是會(huì)產(chǎn)生沖突嗎,所以 update 的時(shí)候肯定要知道最新的數(shù)據(jù)。另外,select ... for update?這種查詢語(yǔ)句是當(dāng)前讀,每次執(zhí)行的時(shí)候都是讀取最新的數(shù)據(jù)。?因此,要討論「可重復(fù)讀」隔離級(jí)別的幻讀現(xiàn)象,是要建立在「當(dāng)前讀」的情況下。接下來(lái),我們假設(shè)select ... for update當(dāng)前讀是不會(huì)加鎖的(實(shí)際上是會(huì)加鎖的),在做一遍讀者那個(gè)實(shí)驗(yàn)。這時(shí)候,事務(wù) B 插入的記錄,就會(huì)被事務(wù) A 的第二條查詢語(yǔ)句查詢到(因?yàn)槭钱?dāng)前讀),這樣就會(huì)出現(xiàn)前后兩次查詢的結(jié)果集合不一樣,這就出現(xiàn)了幻讀。所以,Innodb 引擎為了解決「可重復(fù)讀」隔離級(jí)別使用「當(dāng)前讀」而造成的幻讀問(wèn)題,就引出了 next-key 鎖,就是記錄鎖和間隙鎖的組合。
  • 記錄鎖,鎖的是記錄本身;

  • 間隙鎖,鎖的就是兩個(gè)值之間的空隙,以防止其他事務(wù)在這個(gè)空隙間插入新的數(shù)據(jù),從而避免幻讀現(xiàn)象。

比如,下面事務(wù) A 查詢語(yǔ)句會(huì)鎖住(2, ∞]范圍的記錄,然后期間如果有其他事務(wù)在這個(gè)鎖住的范圍插入數(shù)據(jù)就會(huì)被阻塞。next-key 鎖的加鎖規(guī)則其實(shí)挺復(fù)雜的,在一些場(chǎng)景下會(huì)退化成記錄鎖或間隙鎖,我之前也寫一篇加鎖規(guī)則,詳細(xì)可以看這篇「我做了一天的實(shí)驗(yàn)!」需要注意的是,next-key lock 鎖的是索引,而不是數(shù)據(jù)本身,所以如果 update 語(yǔ)句的 where 條件沒(méi)有用到索引列,那么就會(huì)全表掃描,在一行行掃描的過(guò)程中,不僅給行加上了行鎖,還給行兩邊的空隙也加上了間隙鎖,相當(dāng)于鎖住整個(gè)表,然后直到事務(wù)結(jié)束才會(huì)釋放鎖。所以在線上千萬(wàn)不要執(zhí)行沒(méi)有帶索引條件的 update 語(yǔ)句,不然會(huì)造成業(yè)務(wù)停滯,我有個(gè)讀者就因?yàn)楦闪诉@個(gè)事情,然后被老板教育了一波,詳細(xì)可以看這篇「完蛋,公司被一條 update 語(yǔ)句干趴了!」好了,這次就聊到這啦,有不明白的地方歡迎討論!??

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉