1. 程式人生 > >淺析MySQL資料庫在InnoDB儲存引擎下的READ COMMITTED與REPEATABLE READ隔離級別以及不可重複讀與幻讀現象

淺析MySQL資料庫在InnoDB儲存引擎下的READ COMMITTED與REPEATABLE READ隔離級別以及不可重複讀與幻讀現象

一、InnoDB儲存引擎下的一致性非鎖定讀與一致性鎖定讀

MySQL在InnoDB引擎下對於READ COMMITTED與REPEATABLE READ這兩個隔離級別均採用的是一致性非鎖定讀,即所謂的多版本併發控制協議(MVCC),這樣增加了併發效能。這兩種隔離級別下預設的讀操作都是讀快照,但是讀取的快照版本不一樣,對於READ COMMITTED讀取的是最新的快照版本,所以一個事務對資料的修改在其提交後對另一個事務是可見的,正是因為其讀取的是最新版的快照,但是對於REPEATABLE READ隔離級別下事務讀取的快照永遠是事務剛開啟的那個舊的快照版本,這樣就不會讀取到另一個事務新提交的資料,不會引起不可重複讀與幻讀現象,至於不可重複讀與幻讀待會會解釋。

而MySQL可以顯示的採用如下兩種方式進行一致性鎖定讀,分別加的是共享鎖(S)與排它鎖(X):

SELECT * FROM table LOCK IN SHARED MODE

SELECT * FROM table FOR UPDATE

二、什麼是不可重複讀與幻讀

不可重複讀與幻讀都是指同一個事務在兩次讀取記錄時出現不一致的情況,造成不一致的原因是其間另一個事務對查詢的記錄進行了修改。但是不可重複讀一般是針對同一條記錄而言,幻讀是針對不同的記錄而言,一般是範圍查詢。

三、READ COMMITTED隔離級別下的不可重複讀現象(採用一致性非鎖定讀)

1、設定READ COMMITTED隔離級別,開啟事務A,查詢表t中a為1的記錄

2、設定READ COMMITTED隔離級別,開啟事務B,更改a等於1的記錄

3、在事務A中再次查詢a為1的記錄得到不同的結果

上面就是READ COMMITTED隔離級別下的所謂的不可重複讀問題。

四、在READ COMMITTED隔離級別下采用一致性鎖定讀解決不可重複讀現象,但存在幻讀現象

對於上面出現的不可重複讀問題使用者可以人為的採用一致性鎖定讀來解決,具體看下面:

1、還是在READ COMMITTED隔離級別下面,事務A採用一致性鎖定讀來獲取a為1的記錄

2、設定READ COMMITTED隔離級別,開啟事務B,更改a等於1的記錄

此時發現修改不了,出現了鎖等待超時。

3、在事務A中再次查詢a為1的記錄得到相同的結果

上面就是採用一致性鎖定讀解決了READ COMMITTED 隔離級別下出現的不可重複讀問題,但是這解決不了幻讀問題,具體事例見下面分析。

1、還是在READ COMMITTED隔離級別下面,事務A採用一致性鎖定讀來獲取a在1到5這個範圍內的記錄

2、設定READ COMMITTED隔離級別,開啟事務B,插入新的記錄2

3、在事務A中再次查詢a在1到5這個範圍內的記錄,發現多了一條記錄,就好像幻讀了一樣

上面能夠解決不可重複讀不能解決幻讀的原因:對於READ COMMITTED隔離級別,在進行範圍讀時,由於唯一性約束,系統採用的是record lock,也就是隻對記錄加鎖,所以不能更改記錄,即能夠解決不可重複讀的問題。但是記錄2不在原本的記錄當中,是可以進行插入的,也就是沒法解決幻讀問題。

五、REPEATABLE READ隔離級別下采用多版本併發控制解決不可重複讀與幻讀(一致性非鎖定讀)

1、在REPEATABLE READ隔離級別下面,事務A採用一致性非鎖定讀來獲取a為1的記錄

2、設定REPEATABLE READ隔離級別,開啟事務B,更改a為1的記錄使其值為2並提交

3、在事務A中再次查詢a為1的記錄,發現與之前結果相同

上面的情況就是在一致性非鎖定讀的情況下REPEATABLE READ隔離級別解決不可重複讀現象,其採用的是多版本併發控制協議(MVCC),讀取的永遠都是事務剛開始時的快照,也就是歷史資料,所以不會看到另一個事務已提交了的資料。

上面MVCC同樣也解決了幻讀的問題:

1、在REPEATABLE READ隔離級別下面,事務A採用一致性非鎖定讀來獲取a在1到5之間的記錄

2、設定REPEATABLE READ隔離級別,開啟事務B,插入a為2的新紀錄並提交

3、在事務A中再次查詢a在1到5之間的記錄,發現與之前結果相同

上面解決幻讀也是通過多版本併發控制實現的,即讀取的是快照歷史資料,對另一個事務提交的資料不可見。

六、REPEATABLE READ隔離級別下采用一致性鎖定讀解決不可重複讀與幻讀

1、在REPEATABLE READ隔離級別下面,事務A採用一致性鎖定讀來獲取a為1的記錄

2、設定REPEATABLE READ隔離級別,開啟事務B,更改a為1的記錄使其值為2並提交

可以看到出現了鎖等待超時,記錄修改不能成功,所以還是原來的記錄

3、在事務A中再次查詢a為1的記錄,發現與之前結果相同

上面就是採用一致性鎖定讀中的鎖解決的不可重複讀,在獲取記錄1時加了一個共享鎖S,此時想修改該記錄,就必須在該記錄上申請排它鎖X,而排它鎖X與共享鎖S是不相容的,所以出現鎖等待,事務A一直沒有提交,最後事務B就會鎖等待超時。

此時採用加鎖的方式仍然可以解決幻讀問題:

1、在REPEATABLE READ隔離級別下面,事務A採用一致性非鎖定讀來獲取a在1到5之間的記錄

2、設定REPEATABLE READ隔離級別,開啟事務B,插入a為2的新紀錄並提交

這個地方就和上面在READ COMMITTED隔離級別下有所不同了,上面READ COMMITTED隔離級別下是可以插入記錄2的,在這裡卻出現了鎖等待,可見InnoDB儲存引擎對這兩種隔離級別下采取的鎖演算法是不一樣的,READ COMMITTED隔離級別下采取的是record lock,即僅鎖住查詢出來的存在的記錄,而這裡是鎖住了一個範圍,採取的是next-key lock,鎖住了負無窮到正無窮的範圍,這裡就算插入的記錄2在這個範圍內,會出現鎖阻塞。

3、在事務A中再次查詢a在1到5之間的記錄,發現與之前結果相同

同樣在REPEATABLE READ 隔離級別下也能通過一致性鎖定讀(next-key lock)解決幻讀問題。

總結:在REPEATABLE READ 隔離級別下,通過next-key lock可以解決不可重複讀與幻讀問題,但是在READ COMMITTED隔離級別下系統加的是record lock只能解決不可重複讀問題,解決不了幻讀問題。