1. 程式人生 > 實用技巧 >mysql事務隔離級別/髒讀/不可重複讀/幻讀詳解

mysql事務隔離級別/髒讀/不可重複讀/幻讀詳解

一、四種事務隔離級別

1.1read uncommitted 讀未提交

即:事務A可以讀取到事務B已修改但未提交的資料。

除非是文章閱讀量,每次+1這種無關痛癢的場景,一般業務系統沒有人會使用該事務隔離級別,標準實在太寬鬆了。

1.2read committed 讀已提交(簡稱RC)

即:事務A只能讀取到事務B修改並已提交的資料。

這個級別相對要嚴格一些,至少是要等其它事務把變更提交到db,才能讀取到,聽上去蠻靠譜的。但是有些業務場景,比如會員系統中,如果要在一個事務中,多次讀取使用者身份,判斷是否會員,如果剛開始讀取到該使用者是會員,做了一些邏輯處理,後面又讀到使用者不是會員了,這就有點崩潰,不知道如何繼續。這種希望同1個事務中,關鍵資料不管讀取多次次,結果都一樣,RC級別就不行了。


1.3repeatable read 可重複讀

即:同一個事務中,多次讀取某一行記錄,始終是一樣的值,不管在此期間,其它事務有沒有修改過該資料(不論是否提交)。該級別解決了RC不可重複讀的問題,但是存在幻讀問題(幻讀後面會詳解)。

1.4serializable 序列化

即:一個事務在修改其它資料時,如果有其它事務也想改,必須等前面的事務提交或回滾後,才能繼續。最嚴格的級別,但是效能最低,也幾乎沒人用。

二、髒讀/不可重複讀/幻讀
2.1髒讀

驗證:

a. 找一個mysql環境,建一個測試表t_people,就2列 id ,name

b. 開二個mysql終端,連到db上,為方便講解,這2個終端稱為“終端1”、“終端2”,終端1裡輸入:

setsession transactionisolationlevelreaduncommitted;

start transaction;

即:設定當前會話的隔離級別為"讀未提交"。

終端2裡,輸入:

1

2

starttransaction;

updatet_peoplesetname='xxx'whereid=1;

然後再回到“終端1”,執行

1

selectid,namefromt_peoplewhereid=1;

可以看到,讀取到了未提交的髒資料 。終端2裡,此時如果執行rollback回滾

終端1裡,繼續執行

1

selectid,

namefromt_peoplewhereid=1;

可以發現最新結果,已經是回滾後的資料。很顯然:如果有髒讀問題出現,就更加保證不了“可重複讀”。

2.2不可重複讀

將事務隔離級別設定成read committed(即:讀已提交),可解決髒讀問題,但滿足不了“可重複讀需求”。

驗證方法跟剛才類似,終端1裡輸入:

1

setsessiontransactionisolationlevelreadcommitted;

將級別設定成RC,然後2個終端裡都開啟事務,終端2中,修改一行資料,但是不提交,此時終端1裡應該是讀不到終端2修改的資料。然後終端2提交,終端1才能讀到修改後的資料。終端2如果繼續修改、提交,終端1裡再讀取這1行,將是最新的值。(也就是隻說,只要終端2不斷修改,不斷提交,終端1裡就能讀到這行不同的新值,即:保證不了同1個事務中,同一行資料,多次重複讀取的值不變)

2.3幻讀

將隔離級別繼續調整至Repeatable Read,還是剛才的場景,變成這樣:

事務A對於同一行資料,不管讀多少次,始終是相同的值,完全不理會有沒有其它事務在修改它。有點:“兩耳不聞窗外事,一心只讀聖賢書”的味道。但是這也有問題,比如秒殺訂單系統中,事務A第1次讀取商品庫存,發現還有1個,可以下單,趕緊繼續,但是此時,可能有另一個事務,也在下單,已經提交了訂單,把庫存減為0了,事務A並不知道,因為多次讀取庫存的值是一樣的,還是1,最後仍然把訂單建立了,形成超賣。

驗證方法:

1

setsessiontransactionisolationlevelrepeatableread;

剩下的步驟跟前面類似,就不重複贅述了。 

2.4序列化

從db層面,要想同時解決髒讀、不可重複讀、幻讀,只有序列化這個級別可以做到。

1

setsessiontransactionisolationlevelserializable;

如下圖:終端1設定序列化後,緊接著select xxx where id=1這條語句後,id=1的這行記錄,就被鎖了。

在終端2裡,更新其它記錄(即:id不等於1)可以正常成功,但是更新id=1 時,就會卡住,除非終端1把事務提交或回滾,否則將一直卡著,直到超時失敗。

小結:

隔離級別  存在的問題
讀未提交   髒讀、不可重複讀、幻讀
讀已提交  不可重複讀、幻讀
可重複讀幻讀
序列化效能問題

隔離級別越嚴格,db綜合性能越低。

建議:

大多數情況下,RC(讀已提交)基本上就足夠了,如果併發度高,可以考慮“RC級別+(應用層)分散式鎖”,這樣即能保證資料正確,對db的效能壓力也較低。