來源:漫畫編程



十一國慶長假,朋友圈的朋友已經走向了大江南北,而我卻在公司加班。這時候,組內新來的萌妹實習生過來找我。

師兄,AbstractPayExecutor這個類你修改了代碼嗎?

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

是呀,我剛剛修改了代碼並提交到GitLab上了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

哦,我說的呢,我剛剛看到的一個常量沒有了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

奧。我忘記和你說了。哈哈啊,這不是出現了不可重複讀現象麼。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

嗯?師兄你說的可是數據庫的讀現象?能給我講講嗎,我一直都不是很懂這個知識點。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

好的。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

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

事務隔離級別

我們知道,在數據庫中,事務是要滿足ACID的,即滿足原子性、一致性、持久性以及隔離性。

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

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

奧。你說的這個我明白,但是具體的隔離級別和讀現象有什麼關係呢?

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

不同的隔離級別會導致和解決不同的讀現象,我先給你介紹下都有哪些讀現象吧。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

髒讀

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

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

打個比方,什麼情況下會出現髒讀呢,就是我在我的本地修改了AbstractPayExecutor這個類的代碼,我還沒提交。

爲了更快的知道這個類被我改成了什麼樣,你跑到我的電腦前面閱讀了我修改後的代碼。這就是髒讀了。因爲我沒有提交代碼,說明我隨時可能撤銷剛剛的修改,這時你之前讀取到的數據就是髒數據了。

這種情況下,多個開發者之間的併發性很高,幾乎沒有任何阻塞。師妹想知道代碼最新的是什麼樣,她就讀到了最新代碼。但是,這個代碼我還沒有提交,這就是個髒數據。我們之間的隔離性很差。

這顯然不是我們想要看到的現象啊。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

是的。我們不希望代碼在未提交的狀態下被別人讀到。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

嗯,那髒讀這種情況實在是太不應該了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

是的,接着我們看下提高一點隔離性。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

不可重複讀

爲了解決髒讀問題,我們決定提高一點隔離性,我在修改代碼的時候,不允許她跑到我的電腦前面讀代碼。她只能讀取到我提交後的代碼。

當我需要修改AbstractPayExecutor這個類的時候,我需要告訴師妹讓她等一下,等我提交完代碼她再讀。這就有效的解決了髒讀,因爲師妹讀到的代碼全部都是我已經提交的代碼。

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

嗯,這樣是好了一些,我不會出現髒讀了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

嗯,髒讀沒有了,但是還會存在不可重複讀問題。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

不可重複讀,就是開始我說的那種AbstractPayExecutor種常量被刪除的現象嗎。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

差不多吧。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

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

師妹在閱讀AbstractPayExecutor的代碼,代碼中定義了一個LOGGER常量。然後我修改了代碼,把這個常量刪除了,並提交。師妹更新了代碼繼續閱讀,但是她卻發現LOGGER這個常量沒有了。

兩次讀取,得到的文件內容不一樣。嚴重了影響了她的學習進度。這就是不可重複讀現象。

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

哦,原來這就是不可重複讀。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

嗯嗯,是的。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

師兄,有沒有可能我再讀某個類的代碼的時候,你先不要修改這個類呢?

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

可以呀。這就是進一步提升了隔離性了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

幻讀

爲了讓師妹可以更好的學習代碼。我們約定好,當她閱讀某個類的代碼的時候,她通知我以下,然後我就不修改這個類的代碼。避免出現不可重複讀的情況。

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

就這樣相安無事了一段時間,師妹又來找我了。

師兄呀,你又新增了幾個類了麼,剛剛我看的時候只有20幾個類呀。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

嗯嗯,是的呀,我又增加了2個枚舉類和1個Java類。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

好吧,那我只能繼續閱讀這幾個新增的類了。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

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

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

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

哦,我明白了。原來這就是幻讀。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

是的,幻讀的情況其實也是可以解決的。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

我知道如何解決,就是我讀代碼的時候你不要做任何修改。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

額,是的。

如何給新來的師妹解釋什麼是數據庫的髒讀、不可重複讀和幻讀

如果想要解決幻讀問題,那麼就只能在師妹閱讀代碼的時候,我什麼也不做了。這樣我們之間的隔離性最高,但是併發性就最低了。

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

總結

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

髒讀指讀到了未提交的數據。

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

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

通過在寫的時候加鎖,可以解決髒讀。

通過在讀的時候加鎖,可以解決不可重複讀。

通過串行化,可以解決幻讀。

以上這幾種解決方案其實是數據庫的幾種隔離級別。後續文字在做介紹。

相關文章