一文詳解幻讀、臟讀和不可重復讀
一、簡介
經(jīng)常有面試官提出這么一個問題:什么是臟讀、不可重復讀和幻讀?
關(guān)于這個問題,我們還得從數(shù)據(jù)庫的管理系統(tǒng)說起,當數(shù)據(jù)庫管理系統(tǒng)在寫入或者更新數(shù)據(jù)的過程中,為了保證數(shù)據(jù)是正確可靠的,需要滿足四個特性:原子性、一致性、隔離性和持久性,簡稱 ACID !
- Atomicity(原子性):一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務在執(zhí)行過程中發(fā)生錯誤,能被恢復(Rollback)到事務開始前的狀態(tài),就像這個事務從來沒有執(zhí)行過一樣。
- Consistency(一致性):在事務開始之前和事務結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入之前和寫入之后的數(shù)據(jù)必須完全符合預期設定的結(jié)果。
- Isolation(隔離性):數(shù)據(jù)庫允許多個并發(fā)事務同時對其數(shù)據(jù)進行讀寫和修改的能力,隔離性可以防止多個事務并發(fā)執(zhí)行時由于交叉執(zhí)行而導致數(shù)據(jù)的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(Serializable)。
- Durability(持久性):事務處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。
例如以銀行轉(zhuǎn)賬為例,從原賬戶扣除金額,以及向目標賬戶添加金額,這兩個階段的操作,被視為一個完整的邏輯過程,不可拆分,簡單的說,要么全部成功,要么全部失敗!這個過程被稱為一個事務,具有 ACID 四個特點!
說了這么多,跟我們今天要說的臟讀、不可重復讀和幻讀有什么關(guān)系呢?
我們都知道,當下主流的數(shù)據(jù)庫,都支持多個事務并發(fā)執(zhí)行,當一個事務在寫入數(shù)據(jù),另一個事務也要讀這條數(shù)據(jù),會出現(xiàn)哪些問題?當一個事務在寫入數(shù)據(jù),另一個事務也要寫入這條數(shù)據(jù),又會發(fā)生什么哪些問題?
當多個事務并發(fā)處理同一條數(shù)據(jù)時,如果事務隔離性不合理,就會產(chǎn)生我們今天要介紹的內(nèi)容,具體的說就是:臟讀、不可重復讀和幻讀!
在事務的四個特性里面,其中隔離性總共分為四種級別:由低到高依次為 Read uncommitted 、Read committed 、Repeatable read 、Serializable ,這四個級別可以逐個解決臟讀 、不可重復讀 、幻讀等這幾類問題。
- read uncommitted:俗稱讀未提交,指的是一個事務還沒提交時,它做的變更就能被別的事務看到。
- Read committed:俗稱讀提交,指的是一個事務提交之后,它做的變更才會被其他事務看到。
- Repeatable read:俗稱可重復讀,指的是一個事務執(zhí)行過程中看到的數(shù)據(jù),總是跟這個事務在啟動時看到的數(shù)據(jù)是一致的,同時當其他事務在未提交時,變更是不可見的。
- Serializable:俗稱串行化,顧名思義就是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現(xiàn)讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執(zhí)行完成,才能繼續(xù)執(zhí)行。
不同的隔離級別,產(chǎn)生的結(jié)果是不一樣,下面我們一起來具體分析分析!
二、場景分析
2.1臟讀
所謂的臟讀,指的是讀到了其他事務未提交的數(shù)據(jù),未提交意味著這些數(shù)據(jù)可能會保存到數(shù)據(jù)庫,也可能會回滾,不保存到數(shù)據(jù)庫。當這個數(shù)據(jù)發(fā)生了回滾,就意味著這個數(shù)據(jù)不存在,這就是臟讀!
臟讀最大的問題就是可能會讀到不存在的數(shù)據(jù)。比如在上圖中,事務 B 的更新數(shù)據(jù)被事務 A 讀取,但是事務 B 回滾了,更新數(shù)據(jù)全部還原。也就是說事務 A 剛剛讀到的數(shù)據(jù)并沒有存在于數(shù)據(jù)庫中。
從結(jié)果上看,事務 A 讀出了一條不存在的數(shù)據(jù),這個問題比較很嚴重!
當數(shù)據(jù)庫的事務隔離級別為讀未提交,就會發(fā)生臟讀現(xiàn)象!
2.2不可重復讀
不可重復讀,指的是在一個事務內(nèi),最開始讀到的數(shù)據(jù)和事務結(jié)束前的任意時刻讀到的同一批數(shù)據(jù)出現(xiàn)不一致的情況。
比如上圖,事務 A 兩次讀取同一數(shù)據(jù),第一次讀取結(jié)果為 1,當事務 B 修改了數(shù)據(jù)并提交,此時的事務 A 第二次讀取結(jié)果為 2,兩次讀取結(jié)果不一致!
當數(shù)據(jù)庫的事務隔離級別為讀未提交、讀提交時,就會發(fā)生不可重復讀現(xiàn)象!
2.3幻讀
幻讀和不可重復讀,有點類似,但是表達的側(cè)重點不一樣。
例如事務 A 對一個表中的數(shù)據(jù)進行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。此時,突然事務 B 插入了一條數(shù)據(jù)并提交了,當事務 A 提交了修改數(shù)據(jù)操作之后,再次讀取全部數(shù)據(jù),結(jié)果發(fā)現(xiàn)還有一條數(shù)據(jù)未更新,給人感覺好像產(chǎn)生了幻覺一樣。這就是幻讀!
當有別的事務,在插入或者刪除同一條數(shù)據(jù)的時候,就容易產(chǎn)生幻讀的現(xiàn)象!
當數(shù)據(jù)庫的事務隔離級別為讀未提交、讀提交、可重復讀時,就會發(fā)生幻讀現(xiàn)象!
三、如何解決
為了解決上述問題,數(shù)據(jù)庫通過鎖機制來解決并發(fā)訪問的問題。
以 Mysql 為例,根據(jù)鎖定對象不同,分為:行級鎖和表級鎖;根據(jù)并發(fā)事務鎖定的關(guān)系上看,分為:共享鎖定和獨占鎖定。
共享鎖定會防止獨占鎖定,但允許其他的共享鎖定;而獨占鎖定既防止共享鎖定也能防止其他獨占鎖定;為了更改數(shù)據(jù),數(shù)據(jù)庫在進行更改的行上施加了行級獨占鎖定,insert、update、delete和selsct for update語句都會隱式采用必要的行鎖定,當沖突加劇,會上升到表級鎖定,此時會影響到其他表的訪問操作。
直接使用鎖機制管理是很復雜的,基于鎖機制,數(shù)據(jù)庫給用戶提供了不同的事務隔離級別,只要設置了事務隔離級別,數(shù)據(jù)庫就會分析事務中的 sql 語句然后自動選擇合適的鎖,可以依次有效的解決臟讀、不可重復讀和幻讀問題!
整體的來說,事務的隔離級別和數(shù)據(jù)庫并發(fā)性是成反比的,隔離級別越高,并發(fā)性越低。
四、小結(jié)
本位主要圍繞什么是臟讀、不可重復讀和幻讀,進行了簡單的介紹,如果有些地方理解不到位,歡迎網(wǎng)友指出!
五、參考
1.程序員那點事 - 一文詳解臟讀、不可重復讀、幻讀
2.數(shù)據(jù)庫事務隔離級別