來自:漫話程式設計(微訊號:mhcoding)
長假,朋友圈的朋友已經走向了大江南北,而我卻在公司加班。這時候,組內新來的萌妹實習生過來找我。
師兄,AbstractPayExecutor這個類你修改了程式碼嗎?
是呀,我剛剛修改了程式碼並提交到GitLab上了。
哦,我說的呢,我剛剛看到的一個常量沒有了。
奧。我忘記和你說了。哈哈啊,這不是出現了不可重覆讀現象麼。
嗯?師兄你說的可是資料庫的讀現象?能給我講講嗎,我一直都不是很懂這個知識點。
好的。
“讀現象”是多個事務併發執行時,在讀取資料方面可能碰到的狀況。瞭解它們有助於理解各隔離級別的含義。其中包括臟讀、不可重覆讀和幻讀。
事務隔離級別
我們知道,在資料庫中,事務是要滿足ACID的,即滿足原子性、一致性、永續性以及隔離性。
在資料庫事務的ACID四個屬性中,隔離性是一個最常放鬆的一個。可以在資料操作過程中利用資料庫的鎖機制或者多版本併發控制機制獲取更高的隔離等級。
但是,隨著資料庫隔離級別的提高,資料的併發能力也會有所下降。所以,如何在併發性和隔離性之間做一個很好的權衡就成了一個至關重要的問題。
奧。你說的這個我明白,但是具體的隔離級別和讀現象有什麼關係呢?
不同的隔離級別會導致和解決不同的讀現象,我先給你介紹下都有哪些讀現象吧。
臟讀
第一種讀現象叫做臟讀,顧名思義,就是讀到了臟資料,即無效資料。
臟讀。是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交(commit)到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料,那麼另外一個事務讀到的這個資料是臟資料,依據臟資料所做的操作可能是不正確的。
打個比方,什麼情況下會出現臟讀呢,就是我在我的本地修改了AbstractPayExecutor這個類的程式碼,我還沒提交。
為了更快的知道這個類被我改成了什麼樣,你跑到我的電腦前面閱讀了我修改後的程式碼。這就是臟讀了。因為我沒有提交程式碼,說明我隨時可能撤銷剛剛的修改,這時你之前讀取到的資料就是臟資料了。
這種情況下,多個開發者之間的併發性很高,幾乎沒有任何阻塞。師妹想知道程式碼最新的是什麼樣,她就讀到了最新程式碼。但是,這個程式碼我還沒有提交,這就是個臟資料。我們之間的隔離性很差。
這顯然不是我們想要看到的現象啊。
是的。我們不希望程式碼在未提交的狀態下被別人讀到。
嗯,那臟讀這種情況實在是太不應該了。
是的,接著我們看下提高一點隔離性。
不可重覆讀
為瞭解決臟讀問題,我們決定提高一點隔離性,我在修改程式碼的時候,不允許她跑到我的電腦前面讀程式碼。她只能讀取到我提交後的程式碼。
當我需要修改AbstractPayExecutor這個類的時候,我需要告訴師妹讓她等一下,等我提交完程式碼她再讀。這就有效的解決了臟讀,因為師妹讀到的程式碼全部都是我已經提交的程式碼。
但是,提高了我們之間的隔離性,併發性就降低了一些。因為她要等我提交程式碼後才能閱讀。
嗯,這樣是好了一些,我不會出現臟讀了。
嗯,臟讀沒有了,但是還會存在不可重覆讀問題。
不可重覆讀,就是開始我說的那種AbstractPayExecutor種常量被刪除的現象嗎。
差不多吧。
我們已經提高了一點隔離級別,使得臟讀現象沒有了。但是並沒有辦法避免以下現象:
師妹在閱讀AbstractPayExecutor的程式碼,程式碼中定義了一個LOGGER常量。然後我修改了程式碼,把這個常量刪除了,並提交。師妹更新了程式碼繼續閱讀,但是她卻發現LOGGER這個常量沒有了。
兩次讀取,得到的檔案內容不一樣。嚴重了影響了她的學習進度。這就是不可重覆讀現象。
不可重覆讀。是指在資料庫訪問中,一個事務範圍內兩個相同的查詢卻傳回了不同資料。這是由於查詢時系統中其他事務修改的提交而引起的。比如事務T1讀取某一資料,事務T2讀取並修改了該資料,T1為了對讀取值進行檢驗而再次讀取該資料,便得到了不同的結果。
哦,原來這就是不可重覆讀。
嗯嗯,是的。
師兄,有沒有可能我再讀某個類的程式碼的時候,你先不要修改這個類呢?
可以呀。這就是進一步提升了隔離性了。
幻讀
為了讓師妹可以更好的學習程式碼。我們約定好,當她閱讀某個類的程式碼的時候,她通知我以下,然後我就不修改這個類的程式碼。避免出現不可重覆讀的情況。
這樣,我們之間的併發性就又降低了一些。不僅僅她閱讀哪個類有了一些限制,我修改哪個類也有了要求了。
就這樣相安無事了一段時間,師妹又來找我了。
師兄呀,你又新增了幾個類了麼,剛剛我看的時候只有20幾個類呀。
嗯嗯,是的呀,我又增加了2個列舉類和1個Java類。
好吧,那我只能繼續閱讀這幾個新增的類了。
在提升了隔離性之後,雖然我不會修改師妹正在閱讀的類,師妹也不會閱讀我正在修改的類。但是我可能會增加或者刪除幾個類。這時候和師妹之前讀取到的類的總個數就有了變化。也就是說,她之前讀到的資料就不準確了。這就是幻讀。
幻讀。指同一個事務內多次查詢傳回的結果集不一樣(比如增加了或者減少了行記錄)。比如同一個事務A內第一次查詢時候有n條記錄,但是第二次同等條件下查詢卻又n+1條記錄,這就好像產生了幻覺。
幻讀是不可重覆讀的一種特殊場景。
哦,我明白了。原來這就是幻讀。
是的,幻讀的情況其實也是可以解決的。
我知道如何解決,就是我讀程式碼的時候你不要做任何修改。
額,是的。
如果想要解決幻讀問題,那麼就只能在師妹閱讀程式碼的時候,我什麼也不做了。這樣我們之間的隔離性最高,但是併發性就最低了。
要想解決臟讀、不可重覆讀、幻讀等讀現象,那麼就需要提高事務的隔離級別。但與此同時,事務的隔離級別越高,併發能力也就越低。所以,還需要讀者根據業務需要進行權衡。
總結
事務的隔離性上,從低到高可能產生的讀現象分別是:臟讀、不可重覆讀、幻讀。
臟讀指讀到了未提交的資料。
不可重覆讀指一次事務內的多次相同查詢,讀取到了不同的結果。
幻讀師不可重覆讀的特殊場景。一次事務內的多次範圍查詢得到了不同的結果。
透過在寫的時候加鎖,可以解決臟讀。
透過在讀的時候加鎖,可以解決不可重覆讀。
透過序列化,可以解決幻讀。
以上這幾種解決方案其實是資料庫的幾種隔離級別。後續文字在做介紹。
朋友會在“發現-看一看”看到你“在看”的內容