什麼是髒讀、不可重複讀和幻讀

咱們知道,事務是一種保障數據原子性、一致性、隔離性和持久性(ACID)的重要機制。
可是用了事務就是安全的嗎?用了事務就能100%正確的讀寫數據了嗎?
若是你沒用搞懂數據庫的隔離機制就盲目使用事務,最終會致使沒法確保數據一致性又或者沒法對應高併發場景下的數據讀寫等等問題。mysql

髒讀、不可重複讀、幻讀是如何發生的

讓咱們來看一些例子,在隔離等級爲Read uncommitted(咱們後面再說)的狀況下執行如下事務操做。 由於沒有選擇適當的隔離方法,這些操做最終都會發生期待結果和實際結果不同的狀況。面試

髒讀

T1讀取了T2未提交的數據。

不可重複讀

T1雖然讀取到了T2提交的數據,但卻不是T1事務開啓時的數據。

幻讀

T1雖然讀取到了T2提交的數據,但這個數據是T1事務開啓時不存在的數據。

若是這是一個我的網站,並且用戶數很少,每一個用戶的請求都在不一樣時間發生,那麼上述的狀況基本上是不會發生的。 可是若是這是一個高併發場景,並且讀寫的都是熱數據,那麼讀錯或者寫錯數據的可能性就會大大增長。sql

如何防止髒讀、不可重複讀、幻讀

有人可能要問了,那麼這些狀況要怎麼避免呢?數據庫

主流的關係型數據庫都提供了隔離等級,以MySQL爲例:安全

MySQL的隔離等級

MySQL提供了4種隔離等級,等級越高越能保持讀寫的一致性。 但爲了保持一致性也付出了必定的成本。好比說串行化(Serializable),它要求事務序列化執行,好比一個事務在寫的時候其餘事務不能讀。在高併發場景下對系統的負載是比較高的。 併發

隔離等級:READ COMMITTED

隔離等級:REPEATABLE READ

不一樣的隔離等級的實現方法

重點來了,講了這麼多概念和現象,若是面試官問
爲何READ COMMITTED能夠避免髒讀?
爲何REPEATABLE READ能夠避免不可重複讀?
你能答上來嗎?高併發

多版本併發控制(Multiversion Concurrency Control)

在MySQL的表中除了用戶定義的列之外還會有隱藏列。post

列名 長度(字節) 做用
DB_TRX_ID 6 插入或更新行的最後一個事務的事務標識符。(刪除視爲更新,將其標記爲已刪除)
DB_ROLL_PTR 7 寫入回滾段的撤消日誌記錄(若行已更新,則撤消日誌記錄包含在更新行以前重建行內容所需的信息)
DB_ROW_ID 6 行標識(隱藏單調自增id)

以READ COMMITTED爲例,

每當事務對數據進行修改的時候都會將舊的數據會被備份到undo日誌中, 而後更新數據庫, 再將新數據的DB_ROLL_PTR指向undo日誌中被複制的那一條數據。網站

每當同一條數據被不一樣事務修改時會重複上面的操做, 最終會造成一個經過DB_ROLL_PTR不斷指向舊數據的版本鏈。3d

而T1之因此可以讀到Empty是由於它讀取的不是數據庫的最新內容,而是版本鏈中和TRX_ID == 1的最新狀態的數據。

下面這篇文章對MVCC作了詳細的介紹,這裏就不作贅述了。
推薦閱讀:MySQL事務隔離級別和MVCC

記憶方法

髒讀

「髒」是相對於「乾淨」而言的。「乾淨」的數據就是已經提交了的數據,而「髒」數據是提交以前的數據。讀到了提交以前的數據就叫髒讀。

不可重複讀

重複說的是每一次讀取某一條數據結果都是同樣的。 不可重複讀就是說在一次事務中,兩次讀取的結果不一樣,說明在兩次讀取的過程當中有別的事務修改了這條記錄。

幻讀

幻即幻覺,就是讀到了本來不存在的數據。 那麼爲何會讀到不存在的數據呢,由於在事務過程當中別的事務插入了一條記錄致使讀到了本來不存在的數據。

參考文章

髒讀、幻讀與不可重複讀
MySQL事務隔離級別和MVCC
MySQL中InnoDB的多版本併發控制(MVCC)

相關文章
相關標籤/搜索