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