mysql: 事務的隔離級別

mysql 實現了以下4種事務的隔離級別,由低到高排序,越低的隔離級別意味著更低的系統開銷 和更高的並發度,但同時也有很多問題:

先簡單介紹一下什麼是臟讀,不可重複讀,幻讀;一句話概括臟讀就是一個事務讀取到另一個事務的中間狀態的數據;不可重複讀就是一個事務讀取到另一個事務提交後更新的數據,導致該事務前後兩次讀取的數據不一致;一個事務在讀取某個範圍內的數據時由於另一個事務在該範圍內添加或刪除了某些數據,導致該事務前後兩次讀取的數據量不一致。沒有聽明白?沒有關係下面我們會一一測試不同的隔離級別帶來的問題。

首先我們查詢當前mysql的隔離級別:

select @@GLOBAL.tx_isolation, @@session.tx_isolation;

當前的隔離級別是 可重複讀 這是mysql的默認隔離級別,我們先將當前session隔離級別設置為 讀未提交:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

注意: 我們最好用mysql的command line 來做測試,其它的第三方的客戶端可能做一些額外的事,影響測試效果。

設置為 讀未提交 後,我們在事務 A 中更新了一條數據,但還未commit, 但在事務 B中卻讀取到這條更新後的數據,出現了 臟讀 的問題:

其實很好理解 讀未提交 顧名思義它能讀取到未提交的數據,自然就會出現臟讀的問題,而臟讀是最基本的保障,連臟讀都有問題必然會有不可重複讀和幻讀的問題。所以我們發現讀未提交的隔離性太差,一般不會使用該隔離級別。然後我們將事務的隔離級別設置為 讀已提交 ,再測試

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

注意: 記得將剛剛的事務都提交,避免影響後續的測試結果。

我們發現設置為讀已提交後,臟讀的問題已經解決了。但是我們發現,我們在事務 A 中修改一條數據並commit之後,在事務 B 中讀取到了 A 更新後的數據,導致事務 B 前後兩次讀取的數據不一致,這就是不可重複讀的問題。

這個也很好理解,讀已提交,意味著它能讀取到另一個事務提交後的結果,所以很自然的避免了臟讀的問題,但有不可重複讀和幻讀的問題。該級別的隔離性也不強,我們一般也不使用。

注意: 記得將剛剛的事務都提交,避免影響後續的測試結果。

然後我們將事務的隔離級別設置為 可重複讀 ,再測試

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

我們發現設置為 可重複讀 後,不可重複讀的問題已經解決了。但是該隔離級別還存在 幻讀 的問題,注意幻讀出現的條件是,我們在事務 A 中對數據進行了 插入或刪除,並且在事務 B 中對事務 A 中插入或刪除的數據進行了修改。

我們在事務A插入了一條新的數據 , 在事務B中更新到了這條數據,然後我們再一次查詢時發現,事務B中多出了一條數據,事務 B 中就出現了幻讀的問題:

雖然該隔離級別還有幻讀的問題,但由於幻讀出現的條件較苛刻且我們需要權衡性能方面的因素,所以MySQL默認使用該隔離級別。

注意: 記得將剛剛的事務都提交,避免影響後續的測試結果。重要的事情說3遍........

最後我們將事務的隔離級別設置為最高的 serializable,它強制事務排序,既事務只能串列話執行,從而解決 幻讀的問題,但這意味著極低的性能。

set session transaction isolation level serializable;

通過事務的隔離級別的設計我們也發現,世上永遠沒有完美的設計只有最合適的設計,當我們在做設計的時候就是在做權衡與取捨。


推薦閱讀:

資料庫事務與MySQL事務總結
樂觀鎖和 MVCC 的區別?

TAG:MySQL | 事务并发 | SQL |