面試官:什么是臟讀、幻讀、不可重復讀?說一說MySQL數據庫中的事務隔離級別是怎樣的?
什么是臟讀、幻讀、不可重復讀?
臟讀、幻讀和不可重復讀是數據庫中常見的并發(fā)訪問問題,它們描述了在多個事務并發(fā)執(zhí)行時可能出現的數據讀取問題。
臟讀(Dirty Read)
指的是一個事務讀取了另一個事務尚未提交的數據。
當事務B修改了數據但尚未提交時,事務A讀取到了這個未提交的數據。如果事務B最終回滾了,那么事務A讀取到的數據就是臟數據。
臟讀可能導致事務A基于不正確的數據做出決策,從而產生錯誤的結果。
圖片
不可重復讀(Nonrepeatable Read)
指的是在一個事務內,多次讀取同一數據時,得到的結果不一致。
例如,事務A第一次讀取數據時,得到了某個值,但在事務A的執(zhí)行過程中,事務B修改了這個值并提交了。
當事務A第二次讀取同一數據時,得到的值與第一次讀取時不同。
不可重復讀可能導致事務A在同一事務內基于不一致的數據做出決策,從而產生錯誤的結果。
幻讀(Phantom Read)
指的是在一個事務內,多次執(zhí)行相同的查詢時,得到的結果集不一致。
例如,事務A第一次查詢時得到了一組數據,但在事務A的執(zhí)行過程中,事務B插入了符合第一次查詢條件的新數據并提交了。
當事務A第二次查詢相同條件時,得到的結果集中出現了新增的數據,就好像產生了幻覺一樣。
幻讀可能導致事務A在同一事務內處理了不一致的數據集,從而產生錯誤的結果。
圖片
重點區(qū)別一下不可重復讀和幻讀:
不可重復讀關注,事務內讀取到的,數據值內容發(fā)生的變化,而幻讀關注,事務內執(zhí)行相同查詢時,結果集數量發(fā)生變化。
一句話總結臟讀、不可重復讀、幻讀:
臟讀:讀取未提交的數據。
不可重復讀:讀取數據期間,數據被其他事務修改,導致再次讀取時結果不同。
幻讀:在范圍查詢期間,有其他事務插入或刪除了記錄,導致查詢結果的數量不一致。
這些問題的出現是由于并發(fā)事務訪問數據庫時的隔離性不足所導致的。
如何解決這些事務訪問并發(fā)問題?
為了解決這些問題,數據庫系統提供了不同的事務隔離級別,不同的隔離級別提供了不同的解決方案,以確保事務的隔離性和數據的一致性。
數據庫系統中的事務隔離級別有哪些?
SQL-92 標準定義了 4 種隔離級別來解決臟讀、幻讀、不可重復讀等這些異常情況,從高到底依次為:
可串行化(Serializable)、可重復讀(Repeatable reads)、讀已提交(Read committed)、讀未提交(Read uncommitted)。
1. 讀未提交(RU):
最低的隔離級別,在這種事務隔離級別下,允許一個事務讀取另一個事務尚未提交的數據。
這可能導致臟讀、不可重復讀和幻讀的問題。
2. 讀已提交(RC):
也可以翻譯成提交讀,在一個事務修改數據過程中,如果事務還沒提交,其他事務不能讀該數據。
這可以避免臟讀問題,但仍可能出現不可重復讀和幻讀的問題。
3. 可重復讀(RR):
在一個事務中,多次讀取同一數據時,得到的結果保持一致。
即使其他事務對數據進行了修改并提交,當前事務讀取的數據也不會發(fā)生變化。
比提交讀更高一個級別的隔離級別,可重復讀可以避免臟讀和不可重復讀問題,但仍可能出現幻讀的問題。
4. 串行化(Serializable):
最高的隔離級別,確保事務之間完全隔離,一個事務執(zhí)行時,其他事務無法對其進行并發(fā)操作。
串行化可以避免臟讀、不可重復讀和幻讀的問題,但會降低并發(fā)性能。
圖片
這些隔離級別按照隔離強度逐漸增強,同時也伴隨著性能的降低。
選擇適當的隔離級別需要根據具體的業(yè)務需求和并發(fā)訪問情況進行權衡。
事務隔離級別又是如何實現的?
事務隔離級別的實現方式可以根據具體的數據庫系統和存儲引擎而有所不同。
1. 鎖機制:
數據庫系統可以使用鎖來實現事務隔離級別。
通過在讀取和修改數據時加鎖,可以確保事務之間的隔離性。
不同的隔離級別可能使用不同類型的鎖,如行級鎖、表級鎖或頁級鎖。
合理使用「共享鎖」「獨占鎖」就可以解決事務間寫入隔離的問題
圖片
MySQL中利用GAP鎖 和 Next-Key實現了不可重復讀的隔離級別。
GAP鎖用于鎖定一個范圍的鍵值之間的間隙,以防止其他事務在該范圍內插入新的索引記錄。
Next-Key鎖是GAP鎖(間隙鎖)和NOT-GAP鎖(精確行鎖)的組合,不僅鎖定了索引記錄本身,還鎖定了索引記錄之前的間隙。
圖片
這樣可以避免不可重復讀問題的發(fā)生,保證事務在讀取范圍內的數據時,其他事務不能在該范圍內插入新的數據。
不過有關「讀」的問題,可以使用效率更高的MVCC解決。
圖片
2. 多版本并發(fā)控制(MVCC)
MVCC (Multi-Version Concurrency Control),即多版本并發(fā)控制,是一種常見的實現事務隔離級別的方式。
它通過為每個事務創(chuàng)建多個版本的隔離快照來實現隔離。
在快照隔離中,每個事務在開始時會創(chuàng)建一個數據快照,事務中的所有讀取操作都基于該快照進行。
每個事務在讀取數據時會看到一個一致性的快照,這個快照是在事務開始時確定的。
3.當有其他事務對數據進行修改時,MVCC又會創(chuàng)建一個新的數據版本,并將新版本的數據與舊版本的數據進行區(qū)分。
這樣,讀取操作可以讀取舊版本的數據,而寫入操作則會寫入新版本的數據,從而實現讀寫操作的并發(fā)性。
MVCC在某些場景中替代了相對低效的「鎖」, 可以避免臟讀和不可重復讀的問題。
圖片
(MVCC 這塊是面試的重難點,這一塊我們后面還會有文章進行詳細的介紹)
4. 串行化執(zhí)行:
雖然江湖中有傳說MVCC可以解決幻讀的問題,但實際上并非如此。
在串行化隔離級別下,事務之間是串行執(zhí)行的,即每個事務在執(zhí)行期間都會鎖定所涉及的數據表,其他事務必須等待該事務完成后才能執(zhí)行。
這種方式才可以避免臟讀、不可重復讀和幻讀的問題,但會降低并發(fā)性能。
圖片
需要注意的是,不同的數據庫系統和存儲引擎可能會有不同的實現方式。
因此,在具體的數據庫系統中,實現事務隔離級別的方式可能會有所不同。這里我們是以MySQL為例進行說明。
介紹一下MySQL中,與事務隔離級別相關的命令
MySQL中與事務隔離級別相關的命令主要有以下兩個:
SET TRANSACTION ISOLATION LEVEL:
該命令用于設置當前會話的事務隔離級別??梢允褂靡韵抡Z法:
SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
其中,READ UNCOMMITTED表示讀未提交,READ COMMITTED表示讀已提交,REPEATABLE READ表示可重復讀,SERIALIZABLE表示串行化。
通過設置不同的隔離級別,可以控制事務的隔離性和并發(fā)訪問的行為。
SELECT @@tx_isolation:
該命令用于查詢當前會話的事務隔離級別。執(zhí)行該命令后,會返回當前會話的事務隔離級別。
圖片
MySQL默認的隔離級別 RR 可重復讀。
命令行開始事務時set autocommit = off 或者 start transaction