1. 程式人生 > >資料庫的髒讀、不可重複讀、幻讀

資料庫的髒讀、不可重複讀、幻讀

一、引言

“讀現象” 是在多個事務併發執行時,在讀取資料方面可能碰到的狀況。瞭解它們有助於理解各隔離級別的含義,其中包括髒讀、不可重複讀和幻讀。

二、事務的隔離級別

我們知道,在資料庫中,事務是要滿足ACID的四個性質,即要滿足原子性、一致性、永續性以及隔離性。

在資料庫事務的ACID四個屬性中,隔離性是一個最常放鬆的一個。可以在資料操作過程中利用資料庫的鎖機制或者多版本併發控制機制獲取更高的隔離等級。

但是,隨著資料庫隔離級別的提高,資料的併發能力也會有所下降。所以,如何在併發性和隔離性之間做一個很好的權衡就成了一個至關重要的問題。

下面,通過例子來理解資料庫的髒讀、不可重複讀、幻讀。假如你們小組入職了一位小白美女新同事,新同事來的第一件事情,就是要熟悉專案程式碼,於是你就很熱情的幫她開了SVN賬號和密碼,把程式碼幫她拉下來,然後讓她熟悉熟悉……

1、髒讀:

第一種讀現象叫做髒讀,顧名思義,就是讀到了髒資料,即無效資料。

髒讀。是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交(commit)到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料,那麼另外一個事務讀到的這個資料是髒資料,依據髒資料所作的操作可能是不正確的。

打個比方:什麼情況下會出現髒讀呢?

就是你在你本地修改了一個PayExecutor這個類的程式碼,但你還沒提交。新同事為了更快的知道這個類被你改成什麼樣,她跑到你的電腦前面閱讀了你修改的程式碼。這就是髒讀了。因為你沒有提交程式碼,說明你隨時可以撤銷剛剛的修改,這時新同事之前讀到的資料就是髒資料了。這種情況下,多個開發者之間的併發性很高,幾乎沒有任何阻塞

,新同事想知道程式碼最新的是什麼樣,她就讀到了最新的程式碼。但是,這個程式碼你還沒有提交,這就是個髒資料,你們之間的隔離性很差。

2、不可重複讀:

為了解決髒讀問題,你們決定提高一個隔離性,規定:你在修改程式碼的時候,不允許新同事跑到你的電腦前面讀程式碼。她只能讀取到你提交後的程式碼。

當你需要修改PayExecutor這個類的時候,你需要告訴新同事讓她等一下,等你提交完程式碼,她再讀。這就有效的解決了髒讀,因為新同事讀到的程式碼全部都是你已經提交的程式碼。

但是,提高了你們之間的隔離性,併發性就降低了一些。因為她要等你提交程式碼後才能閱讀。

好的,你們現在已經提高了一點隔離級別,使得髒讀現象沒有了,但是並沒有辦法避免以下現象:

新同事在閱讀PayExecutor的程式碼時,程式碼中定義了一個LOGGER常量。這時你修改了程式碼,把這個常量刪除了,然後提交了。新同事更新了程式碼繼續閱讀,但是她卻發現LOGGER這個常量沒有了。

兩次讀取,得到的檔案內容不一樣。嚴重影響了新同事的學習進度。這就是不可重複讀現象。

不可重複讀。是指在資料庫訪問中,一個事務範圍內的兩個相同的查詢卻返回了不同資料。這是由於查詢時,系統中其他事務修改的提交而引起的。比如事務T1讀取某一資料,事務T2讀取並修改了該資料,T1為了對讀取值進行檢驗而再次讀取該資料,便得到了不同的結果。

3、幻讀:

為了讓新同事可以更好的學習程式碼。你們約定好,當她閱讀某個類的程式碼的時候,通知你一下,然後你就不修改這個類的程式碼。避免出現了不可重複讀的情況。

這樣,你們之間的併發性就又降低了一些。不僅僅她閱讀哪個類有了一些限制,你修改哪個類也有了要求了。

就這樣相安無事了一段時間,她又來找你了。

在提升了隔離性之後,雖然你不會修改新同事正在閱讀的類,她也不會閱讀你正在修改的類。但是你可能會增加或者刪除幾個類。這時候和新同事之前讀取到的類的總個數就有了變化。

也就是說,她之前讀到的資料就不準確了。這就是幻讀。

幻讀。指同一個事務內多次查詢返回的結果集不一樣(比如增加了或者減少了行記錄)。比如在同一個事務A內,第一次查詢時候有n條記錄,但是第二次同等條件下查詢卻又n+1條記, 這就好像產生了幻覺。

幻讀是不可重複讀的一種特殊場景。

要想解決髒讀、不可重複讀、幻讀等讀現象,那麼就需要提高事務的隔離級別。但與此同時,事務的隔離級別越高,併發能力也就越低。所以,還需要根據業務需求進行權衡。

三、總結:

(1)、事務的隔離性上,從低到高可能產生的讀現象分別是:髒讀、不可重複讀、幻讀。

(2)、髒讀指讀到了未提交的資料。

(3)、不可重複讀指一次事務內的多次相同查詢,讀取到了不同的結果。

(4)、幻讀是不可重複讀的特殊場景。一次事務內的多次範圍查詢得到了不同的結果。

通過在寫的時候加鎖,可以解決髒讀。
通過在讀的時候加鎖,可以解決不可重複讀。
通過序列化,可以解決幻讀。