Innodb RR隔離級(jí)別下到底能否避免幻讀
背景
這個(gè)事情要回溯到曾經(jīng)背八股文的時(shí)候了,想必大家在背八股文的時(shí)候?qū)τ谑聞?wù)隔離級(jí)別都已經(jīng)背得滾瓜爛熟了,一般在說隔離級(jí)別的時(shí)候,都順帶會(huì)提到mysql的innodb的RR隔離級(jí)別,由于他與眾不同的實(shí)現(xiàn)方式,通常會(huì)有下面的一些描述:
在我的腦海里面一直就記著,mysql的Innodb在RR隔離級(jí)別下就能避免幻讀(曾經(jīng)面試的時(shí)候也這樣回答過),但是直到有一天群里的同學(xué)拋出了一個(gè)問題,
我的第一反應(yīng)也是
怎么定義幻讀?
其實(shí)對(duì)于這個(gè)爭(zhēng)論,很多點(diǎn)在于什么才叫做幻讀?先來一個(gè)幻讀的通俗的定義,對(duì)于相同的區(qū)間查詢,插入和刪除操作使得對(duì)相同的區(qū)間查詢操作返回不同的結(jié)果。
在Innodb的RR隔離級(jí)別下,比如我們對(duì)一個(gè)表進(jìn)行(id>1 and i < 100)的刪除操作,另外一個(gè)事務(wù)這個(gè)時(shí)候插入一條id=50的數(shù)據(jù),如果插入成功的話就會(huì)導(dǎo)致我們第一個(gè)事務(wù)出現(xiàn)幻覺,所以在inndodb中使用了next-key lock算法,也就是加了間隙鎖,從而阻止插入意向鎖。
接下來我們?cè)倏匆幌翸ySQL官方定義的幻讀:
翻譯過來其實(shí)就是:當(dāng)同一個(gè)查詢?cè)诓煌臅r(shí)間產(chǎn)生不同的集合時(shí),就會(huì)發(fā)生所謂的幻讀問題。例如,如果一個(gè)SELECT執(zhí)行了兩次,但是第二次返回了第一次沒有返回的行,那么該行就是一個(gè)“幻像”行。
這個(gè)定義和我們開始那個(gè)定義有什么區(qū)別嗎?看起來區(qū)別不大,但是細(xì)細(xì)的品味第一個(gè)定義限制了插入和刪除。在MYSQL的官方定義下,用了兩次查詢,并沒有定義另外一個(gè)事務(wù)做了什么,以及兩次查詢之間發(fā)生了什么,所以出現(xiàn)了這樣的一個(gè)情況:
上面有兩個(gè)事務(wù),事務(wù)B發(fā)生了幻讀的現(xiàn)象,為什么說這里是幻讀的現(xiàn)象呢?因?yàn)榘凑誐ySQL的定義兩次查詢返回不同集合,事務(wù)B的確是發(fā)生了幻讀現(xiàn)象。
為什么會(huì)出現(xiàn)這個(gè)情況呢?這個(gè)主要還是因?yàn)樵趇nnodb下所有的讀都是快照讀,如果我們?cè)谑聞?wù)中對(duì)這個(gè)數(shù)據(jù)加鎖,那么就變成了當(dāng)前讀,所以就能讀取到事務(wù)A寫的數(shù)據(jù)了。這種情況在一些文獻(xiàn)中也被叫做: write skew style phantom。
RR級(jí)別如何解決幻讀?
其實(shí)我們細(xì)細(xì)分析,我們上面那個(gè)情況是怎么解決的幻讀,是依靠next-key lock,而我們第二個(gè)案例雖然在事務(wù)中但是卻沒有使用next-key lock,如果我們真的對(duì)幻讀有很多要求的話,那么我們?cè)诓樵兊臅r(shí)候直接加上select ... for update 加上鎖,這樣可以直接讓我們走當(dāng)前讀,從而避免幻讀的出現(xiàn)。
最后
這篇文章營(yíng)養(yǎng)價(jià)值不高,主要是用來糾正大家一些觀念,有時(shí)候八股文盲目去背沒有細(xì)細(xì)思考,可能就會(huì)導(dǎo)致認(rèn)知上的錯(cuò)誤。最后總結(jié)一下,在RR隔離級(jí)別下只要不出現(xiàn)快照讀和當(dāng)前讀的切換,其實(shí)就能保證不會(huì)出現(xiàn)幻讀。