一直以來對數據庫的事務隔離機制的理解總是停留在表面,其內容也是看一遍忘一邊。這兩天決定從原理上理解它,整理成自己的知識。查閱資料的過程中發現好多零碎的概念如果串起來足夠寫一本書,所以在這裏給自己梳理一個脈絡,具體的內容參考引文或在網上搜一下。由於平時接觸最多的是MySQL,所以文章中某些部分是MySQL特有的特性,請讀者注意。
數據庫併發操作會引發的問題:
- 髒讀(dirty read):A事務讀取B事務尚未提交的更改數據,並在這個數據基礎上操作。如果B事務回滾,那麼A事務讀到的數據根本不是合法的,稱爲髒讀。在oracle中,由於有version控制,不會出現髒讀。
- 不可重複讀(unrepeatable read):A事務讀取了B事務已經提交的更改(或刪除)數據。比如A事務第一次讀取數據,然後B事務更改該數據並提交,A事務再次讀取數據,兩次讀取的數據不一樣。
- 幻讀(phantom read):A事務讀取了B事務已經提交的新增數據。注意和不可重複讀的區別,這裏是新增,不可重複讀是更改(或刪除)。這兩種情況對策是不一樣的,對於不可重複讀,只需要採取行級鎖防止該記錄數據被更改或刪除,然而對於幻讀必須加表級鎖,防止在這個表中新增一條數據。
- 第一類丟失更新:A事務撤銷時,把已提交的B事務的數據覆蓋掉。
- 第二類丟失更新:A事務提交時,把已提交的B事務的數據覆蓋掉。
數據庫在併發操作下會出現上述這些問題,要解決它就要想辦法在執行可能引發問題的操作之前將該操作阻塞住,讓它等到合適的時機再執行。那麼如何挑選合適的時機阻塞操作的執行,又如何保證在調度過程執行完成後其執行結果與串行執行操作的結果相同呢?
三級封鎖協議
數據庫想要在“合適”的時機阻塞住數據庫操作,那麼首先要定義好怎麼樣的時機算是“合適”,因爲各個系統支持的業務千差萬別,對數據的實時性和有效性的要求也不同。於是數據庫理論中就提出了封鎖級別的概念,對不同的同步要求採用不同的封鎖級別。
三級封鎖協議內容如下:
- 一級封鎖協議:事務T在修改數據R之前必須先對其加X鎖,直到事務結束才釋放。事務結束包括正常結束(COMMIT)和非正常結束(ROLLBACK)。 一級封鎖協議可以防止丟失修改,並保證事務T是可恢復的。使用一級封鎖協議可以解決丟失修改問題。在一級封鎖協議中,如果僅僅是讀數據不對其進行修改,是不需要加鎖的,它不能保證可重複讀和不讀“髒”數據。
- 二級封鎖協議:一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,讀完後方可釋放S鎖。 二級封鎖協議除防止了丟失修改,還可以進一步防止讀“髒”數據。但在二級封鎖協議中,由於讀完數據後即可釋放S鎖,所以它不能保證可重複讀。
- 三級封鎖協議 :一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,直到事務結束才釋放。 三級封鎖協議除防止了丟失修改和不讀“髒”數據外,還進一步防止了不可重複讀。
事務隔離級別:
三級封鎖協議反映在實際的數據庫系統上,就是四級事務隔離機制。總的來說,四種事務隔離機制就是在逐漸的限制事務的自由度,以滿足對不同併發控制程度的要求。以下就是數據庫的四種隔離級別:
Read Uncommitted、Read Committed、Repeatable Read、Serializable
其對各個併發問題的制約強度見下表:
√: 可能出現 ×: 不會出現
髒讀 | 不可重複讀 | 幻讀 | |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
四種級別對併發問題的解決由弱到強,相應的系統性能由強到弱,MySQL的默認級別是Repeatable Read。
Read Uncommitted
在Read Uncommitted策略下,數據庫遵循一級封鎖協議,只對修改數據的併發操作做限制。一個事務不能修改其他事務正在修改的數據,但可以讀取到其他事務中尚未提交的修改,這些修改如果未被提交,將會成爲髒數據。
Read committed
在Read committed策略下,數據庫遵循二級封鎖協議,只允許讀取已經被提交的數據,反過來講,如果一個事務修改了某行數據且尚未提交,而第二個事務要讀取這行數據的話,那麼是不允許的。在MySql的InnoDB下,雖然這種操作不被允許,但MySQL不會阻塞住數據的查詢操作,而是會查詢出數據被修改之前的備份,返回給客戶端。MySQL的這種機制稱爲MVCC(多版本併發控制),就是說數據庫在事務併發的過程中對數據維護多個版本,使得不同的事務對不同的數據版本進行讀寫(MVCC的實現參見引用中的文章)。這樣的機制反映在應用中就是,在任何時候對數據庫查詢總是可以得到數據庫中最近提交的數據。爲被提交的髒數據被隔離起來,無法被查詢到,即防止髒讀發生。
Repeat Read
Repeat Read又比Read Committed更加嚴格一點,但仍然是在二級封鎖協議的範疇,只是讀取過程受到更多MVCC的影響。在Read Committed下,允許一個事務中多次相同查詢得到不同的結果,就是所謂的不可重複讀問題。這在一些應用中是允許的,所以oracle、SQL server上默認這一隔離級別,但MySQL沒有,它默認Repeat Read級別。在這一級別下,有賴於MVCC,同一個事務中的查詢只能查到版本號不高於當前事務版本的數據,即事務只能看到該事務開始前或者被該事物影響的數據。反過來說,這一級別下,不允許事務讀取在該事務開始後新提交的數據。即防止了不可重複讀的發生。
依靠上面的機制,已經做到了在事務內數據內容的不變,但是不能保證多次查詢得到的數據數量一致。因爲在一個事務執行的過程中別的事務完全可以執行數據插入,當插入了剛好符合查詢條件的數據時,就會引發數據查詢結果集增加,引發幻讀。還有一種情況就是,如果一個事務想插入一條數據,而另一個事務已經插入了含有相同主鍵的數據,那麼當前事務也會被阻塞,並最終執行失敗,雖然當前事務根本無法查詢到這一條數據,這也是一種幻讀。InnoDB提供的間隙鎖機制可以在一定程度上防止幻讀的發生,具體介紹見最後一篇引文。
Serializable
最後,最強事務隔離機制Serializable,它遵循三級封鎖協議,使得所有的事務必須串行化執行,只要有事務在對錶進行查詢,那麼在此事務提交前,任何其他事務的修改都會被阻塞。這解決了一切併發問題,但會造成大量的等待、阻塞甚至死鎖,使系統性能降低。
要注意,在任何一種隔離機制下,都是不允許一個事務刪除或修改另一個事務影響過而未提交的數據的。因爲事務增、刪、改數據以後,會在該行加上排它鎖,排它鎖會阻塞其他事務再次對該行數據操作。也正是由於排它鎖的存在,這四種隔離機制都不會出現任何一種更新丟失的現象,因爲一條信息根本不允許第二個事務進行修改。
兩段鎖協議
數據庫在調度併發事務時遵循“兩段鎖”協議,“兩段鎖”協議是指所有事務必須分兩個階段對數據項進行加鎖和解鎖
- 擴展階段:在對任何數據項的讀、寫之前,要申請並獲得該數據項的封鎖。
- 收縮階段:每個事務中,所有的封鎖請求必須先於解鎖請求。
在數學上可以證明,遵循兩段鎖的調度可以保證調度結果與串行化調度相同。這樣的機制保證了數據庫併發調度與串行調度的等價。
*注:
參考資料:
http://blog.csdn.net/fg2006/article/details/6937413
http://blog.csdn.net/chen77716/article/details/6742128
http://www.2cto.com/database/201304/201415.html
http://snailxr.iteye.com/blog/1143615
http://blog.sina.com.cn/s/blog_711b11fd0101bhks.html
http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html
轉載請註明出處:
http://blog.csdn.net/gklifg/article/details/38752691
</div>
</div>