Read Committed 為什么不能防止不可重復讀現象


我們都知道MySQL 數組庫有四大事務隔離級別,分別是未提交讀(Read Uncommitted)、提交讀(Read Committed)、可重復度(Repeatable Read)、可串行化(SERIALIZABLE).

其中每個隔離級別有不同的特性

未提交讀可能會導致臟讀,不可重復度和幻讀問題。

提交讀因為只能讀取已經提交的數據,所以可以避免臟讀,但是不保證事務重新讀的時候能讀到相同的數據,因為在每次數據讀完之后其他事務可以修改剛才讀到的數據,所以不能避免不可重復讀和幻讀現象,

可重復讀則可以避免臟讀和不可重復度現象,但是不能避免幻讀現象

可串行化則能同時避免臟讀、不可重復讀和幻讀現象。

相信大家對上面的四個隔離級別的特征都耳熟能詳,但是今天我突然發現一個問題,為什么提交讀不能避免不可重復現象,既然讀的事務還沒有提交,為什么其他事務可以修改剛才讀到的數據?既然一個字段或一條記錄已經被一個事務的讀鎖占據,為什么還會被另一個事務的寫鎖獲取到?

先看一個提交讀的隔離級別下發生不可重復度讀現象的一個例子

① 打開命令客戶端A,將這個客戶端的隔離級別設置為提交讀(Read Committed),然后開啟事務,讀取account表中的數據

第一次讀取account表的數據,money 為1000.00,不提交事務

 ② 開啟命令行客戶端B, 同樣設置事務的隔離級別為提交讀,開啟事務,對account 表中tom的 money 進行修改,不提交事務

客戶端開啟事務后B修改表數據,但是不提交事務

 ③ 客戶端A 再次讀取表中數據,因為客戶端B的事務還未提交,所以客戶端A讀取不到客戶端B修改后的值,讀取到的數據仍是上一次事務提交后的值1000, 這就避免了臟讀

④ 客戶端B提交事務

⑤ 客戶端 A 再次查看,讀取到 2000, 發現和上一次讀取的money值不一樣,同一個事務中前后兩次讀取到的數據不一樣,發生了不可重復度現象

 

  通過上面這個例子,可以看到提交讀的隔離級別下,確實不能避免不可重復讀現象,現在的疑問是,明明客戶端A的事務1已經先對記錄加了讀鎖,為什么客戶端B的事務2可以修改記錄的數據呢?按我們常規的想法不應該是讀鎖和寫鎖互斥嗎?

 原因:

MySQL 中的提交讀和可重復讀兩個隔離級別是使用多版本並發控制 MVCC 來實現的,而不是通過添加讀寫鎖來實現的,如果通過讀寫鎖來實現隔離級別的話,只有讀讀可以並發,讀寫,寫讀,寫寫都不能並發,這樣數據庫的並發度太低了,所以一般不通過加讀寫鎖來實現隔離級別。而如果使用 MVCC 來實現 提交讀和可重復讀兩個隔離級別的話則可以在讀的時候不加鎖,讀寫和寫讀可以同時進行,只有寫寫需要阻塞,這樣就極大地提高了並發度。

MVCC 機制會記錄每行數據的歷史版本,通過可見性算法、undo 日志以及 read view 控制每個讀操作所讀取的行數據歷史版本,

Repeatable Read 在事務發生第一次讀的時候選定所要讀取的數據行的版本,整個事務都讀取這一個版本的數據行,所以可以重復讀,每次讀取的數據都一致。

Read Committed 在事務中每次讀操作都是讀取最新的行數據版本,而這最新的數據行版本很可能是某個事務進行了修改操作后提交的,所以可能會發生多次讀取同一行數據,但是前后讀取的數據不一致的情況。這就是不可重復讀現象,所以提交讀不能避免不可重復度現象。

 

想要詳細了解MVCC是如何實現事務隔離的,可以閱讀這篇博客,MySQL中MVCC的正確打開方式(源碼佐證),寫的非常好,強力推薦。

 

文章參考:

不可重復讀(read-committed)讀已提交例子

mysql事務之提交讀(Read Committed)


免責聲明!

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



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