MySQL(23):事務的隔離級別出現問題之 臟讀


1. 臟讀

  所謂的臟讀就是指一個事務讀取了另一個事務未提取的數據。

試想一下:a賬戶要給b賬戶100元購買商品,如果a賬戶開啟一個事務,執行下面的update語句做了如下轉賬的工作:

update account set money=money-100 where name='a';
update account set money=money+100 where name='b';

如果a賬戶先不提交事務,通知b賬戶來查詢,由於b的隔離級別比較低,此時就會讀取a事務中未提交的數據,發現a確實給自己轉了100元,然后給a發貨,等b發貨成功之后,a再將事務回滾,此時b就會受到損傷,這就是臟讀造成的。

 

為了演示上面的情況,這里我們開啟了兩個命令行窗口(相當於開啟兩個線程),分別模擬a賬戶和b賬戶,如下:

 

(1)設置b賬戶中事務的隔離級別

大家都知道MySQL的默認隔離級別是Repeatable Read(可重復讀),該級別是可以避免臟讀的,因此需要將b賬戶中事務的隔離級別設置為Read Uncommitted(讀未提交),具體語句如下:

set session transaction isolation level read uncommitted;

如下:

 

上述語句之中,session表示當前會話,transaction就表示事務,isolation表示隔離,level表示級別,read uncommitted表示當前的隔離級別,該語句執行成功之后,使用select語句查詢事務的隔離級別,結果如下:

select @@tx_isolation;

如下:

 

從上述結果可以看出,b賬戶的事務隔離級別以及修改為Read Uncommitted,接下來就是演示臟讀的情況

 

(2)演示臟讀

b賬戶:為了證明出現了臟讀的情況,首先在b賬戶中開戶一個事務,並在該事務中查詢當前賬戶的余額信息,查詢結果如下:

start transaction;
select * from account;

如下:

 

 

a賬戶:在a賬戶中開啟一個事務,並在當前窗口中執行轉賬功能,具體語句如下:

start transaction;
update account set money=money-100 where name='a';
update account set money=money+100 where name='b';

如下:

需要注意的是:此時不要提交事務,如果提交事務就無法演示出現臟讀的情況。

 

b賬戶:a賬戶執行完轉賬語句后,b賬戶查詢當前賬戶,如下:

從上面的查詢結果來看,a賬戶已經成功給b賬戶轉賬了100元,這是由於b賬戶的事務隔離級別比較低,因此才讀取了a賬戶還沒有提交的數據內容,出現了臟讀的情況,這時候,b誤以為a賬戶以及轉賬成功,便會給a發貨,當b發貨之后a如果不提交事務將事務回滾,b就會受到損失。

上面演示完畢了,需要將a賬戶中的事務回滾,b賬戶中的事務提交。

 

(3)設置b賬戶的事務隔離級別

為了防止臟讀發生,可以將b賬戶中的事務隔離級別設置為Read Committed(讀提交),該級別會避免臟讀,具體語句如下:

set session transaction isolation level read committed;

上述的語句執行成功之后,b賬戶的隔離級別已經設置成Read Committed

(4)驗證是否出現臟讀

b賬戶:為了說明沒有出現臟讀的情況,首先要在b賬戶中開啟一個事務,並在該事務中查詢各賬戶的余額情況,查詢結果如下:

 

a賬戶:在a賬戶中重新開啟一個事務,實現了轉賬功能,如下:

start transaction; update account set money=money-100 where name='a'; update account set money=money+100 where name='b';

 

b賬戶:當a 賬戶轉賬成功之后,可以在b賬戶中再次查詢各賬戶的余額信息,查詢結果如下:

 通過上面的對比兩次查詢結果可以發現,b賬戶在同一個事務中的查詢結果是一致的,並沒有查詢到a賬戶中未提交的內容,因此可以說明Read Committed 隔離級別可以避免臟讀,最后分別將a賬戶和b賬戶中的事務回滾(方便以后演示)。


免責聲明!

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



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