mysql事務隔離級別、臟讀、幻讀


Mysql事務隔離級別本身很重要,再加上可能是因為各大公司面試必問的緣故,在博客中出現的概率非常高,但不幸的是,中國的技術博客要么是轉載,要么是照抄,質量參差不齊,好多結論都是錯的,對於心懷好奇之心想弄明白問題的同學來說,這些博客是很危險的。我當時也是看了各種版本的博客之后,翻官網,做實驗,最終搞明白了一些事情,寫在這里,希望對后來人有所幫助。

事務隔離級別
先說什么叫做“事務隔離”,事務隔離是指多個事務同時在進行中(如果只有一個事務,那就無所謂隔離不隔離了)時,各個事務被隔離開來,相互之間的影響和事物的隔離級別有關,按照“讀未提交”–>“讀已提交”–>“可重復讀”–>“串行化”的順序,越往后面隔離級別越高,事務之間的影響越小。
下面的內容都以這個數據表為例進行說明:

id money
1 10
2 20
4 40
讀未提交和臟讀
兩個同時進行的事務A、B,A會讀到B還未提交的的數據,這種事務隔離級別會存在臟讀的問題,舉個例子:
B修改id=2的money為200,但是尚未提交,然后A讀取id為2的money,這是讀到的值是200,然后B因為某種原因(比如系統異常)導致事務回滾了,此時2最終的money仍然是20。說明A讀到了臟數據。
    A                        B
                             begin
                    update table set money = 200 where id = 2
select money from table where id = 2
                           rollback

讀已提交和不可重復讀
為了解決臟讀的問題,可以將事務隔離級別調整到“讀已提交”,這種隔離級別下,事務A只能讀到事務B提交后的數據,但是這種隔離級別雖然解決了臟讀的問題,但是無法解決可重復讀的問題,即在事務A中,兩次執行相同的讀取語句,讀到的內容卻是不一樣的。舉例如下
      A                        B
      begin                       begin
                    update table set money = 200 where id = 2
select money from table where id = 2
                           commit
select money from table where id = 2
     commit

事務A中第一次select時,讀到的值是20,第二次再執行select時,讀取到的值是200。這就是不可重復讀。

可重復讀
為了解決不可重復讀的問題,可以將事務隔離級別調整到“可重復讀”,這種隔離級別下,可以保證在同一個事務中只會讀取到當前事務對數據的修改,其他事務修改的數據不會影響到當前事務的任何一次讀取。還是上面的例子,事務A兩次select讀取到的結果都是20,即可重復讀.

幻讀
可重復讀雖然解決了不可重復讀的問題,但是仍然存在幻讀的問題。

錯誤的說法
對於幻讀的解釋,大多數博客中都是有問題的,甚至《高性能mysql》中,也沒有詳細解釋幻讀的問題
我們先來看看網上出現最多的幻讀的解釋,務必注意,這種解釋是錯誤的:
       A                        B
       begin                      begin
select money from table where id > 2
                           inser into table values(3,30)
                              commit
select money from table where id > 2
       commit

網上多數觀點認為,當A進行范圍查詢時,B在其中插入一條數據,A再次執行該范圍查詢是,就會比第一次查詢時多出來B剛才插入的那條數據,像幻影一樣,所以出現了幻讀。
為什么這種說法是不對的呢?
從實踐上看,直接去msql中試試就會發現,其實A連個執行的查詢結果仍然是一樣的,並不會出現幻讀的現象。
從原理上看,可重復讀是靠MVCC(多版本並發控制)保證的,該模式下,保證事務只能讀取到當前事務開啟之前已經提交的事務進行的修改以及當前事務本身對數據的修改。按照這個定義,上述實驗中的B事務並不會影響到A的讀取。

真正的幻讀
那么什么事真正的幻讀呢?來看下面的例子
A                        B
       begin                      begin
select money from table where id > 2
                           inser into table values(3,30)
                              commit
update table set money = 0 where id > 2
select money from table where id > 2
       commit

在B事務提交之后,A事務第二次select之前,先進行一次update操作,然后A再次執行select時,id=3的行就會出現,而且money的值是0.出現了幻讀。

出現幻讀的原因
MVCC只對讀有效,對寫操作無效,由於update是寫操作,所以為更新B已經插入的id=3的行,將money更新成0,此時id=3的行被A事務(當前事務)修改了,所以A事務中第二次select時,是可以看到被當前事務修改(update)的數據的,所以id=3的行會出現在select的結果中,這就是幻讀出現的原因。

串行
串行將所有的事務一個接一個的進行,不存在多個事務同事進行的情況,所以串行的事務隔離級別不會出現上面提到的臟讀、不可重復讀、幻讀等任何一種問題。代價是,嚴重影響了數據庫的性能。

事務隔離級別的選擇
事務隔離級別需要根據具體的業務場景來選擇,並沒有哪一個級別是萬能的。
有些場景下,甚至根本不需要事務,這時候,也許MyIsam引擎才是最合適的。
Oracle的默認事務隔離級別是讀已提交,而mysql的默認事務隔離級別是可重復讀,根據也無需要,可以把mysql的隔離級別調整到“度已提交”。
“讀未提交”和“串行”這兩種隔離級別因為臟讀和性能的問題,業內用的很少。

有需要交流的小伙伴可以點擊這里加本人QQ:luke


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM