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