數據庫中的併發操作帶來的一系列問題及解決方法

數據庫中常見的併發操作所帶來的一致性問題包括:丟失的修改、不可重複讀、讀髒數據、幻影讀(幻影讀在一些資料中往往與不可重複讀歸爲一類)。
丟失修改

下面我們先來看一個例子,說明併發操作帶來的數據的不一致性問題。

考慮飛機訂票系統中的一個活動序列:
甲售票點(甲事務)讀出某航班的機票餘額A,設A=16.
乙售票點(乙事務)讀出同一航班的機票餘額A,也爲16.
甲售票點賣出一張機票,修改餘額A←A-1.所以A爲15,把A寫回數據庫.
乙售票點也賣出一張機票,修改餘額A←A-1.所以A爲15,把A寫回數據庫.

結果明明賣出兩張機票,數據庫中機票餘額只減少1。

歸納起來就是:兩個事務T1和T2讀入同一數據並修改,T2提交的結果破壞了T1提交的結果,導致T1的修改被丟失。前文(2.1.4數據刪除與更新)中提到的問題及解決辦法往往是針對此類併發問題的。但仍然有幾類問題通過上面的方法解決不了,那就是:
 不可重複讀

不可重複讀是指事務T1讀取數據後,事務T2執行更新操作,使T1無法再現前一次讀取結果。具體地講,不可重複讀包括三種情況:
事務T1讀取某一數據後,事務T2對其做了修改,當事務1再次讀該數據時,得到與前一次不同的值。例如,T1讀取B=100進行運算,T2讀取同一數據B,對其進行修改後將B=200寫回數據庫。T1爲了對讀取值校對重讀B,B已爲200,與第一次讀取值不一致。
事務T1按一定條件從數據庫中讀取了某些數據記錄後,事務T2刪除了其中部分記錄,當T1再次按相同條件讀取數據時,發現某些記錄神密地消失了。
事務T1按一定條件從數據庫中讀取某些數據記錄後,事務T2插入了一些記錄,當T1再次按相同條件讀取數據時,發現多了一些記錄。(這也叫做幻影讀) 
讀"髒"數據

讀"髒"數據是指事務T1修改某一數據,並將其寫回磁盤,事務T2讀取同一數據後,T1由於某種原因被撤消,這時T1已修改過的數據恢復原值,T2讀到的數據就與數據庫中的數據不一致,則T2讀到的數據就爲"髒"數據,即不正確的數據。

產生上述三類數據不一致性的主要原因是併發操作破壞了事務的隔離性。併發控制就是要用正確的方式調度併發操作,使一個用戶事務的執行不受其它事務的干擾,從而避免造成數據的不一致性。
併發一致性問題的解決辦法
 封鎖(Locking)

封鎖是實現併發控制的一個非常重要的技術。所謂封鎖就是事務T在對某個數據對象例如表、記錄等操作之前,先向系統發出請求,對其加鎖。加鎖後事務T就對該數據對象有了一定的控制,在事務T釋放它的鎖之前,其它的事務不能更新此數據對象。

基本的封鎖類型有兩種:排它鎖(Exclusive locks 簡記爲X鎖)和共享鎖(Share locks 簡記爲S鎖)。

排它鎖又稱爲寫鎖。若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其它任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。這就保證了其它事務在T釋放A上的鎖之前不能再讀取和修改A。

共享鎖又稱爲讀鎖。若事務T對數據對象A加上S鎖,則其它事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其它事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
封鎖協議

在運用X鎖和S鎖這兩種基本封鎖,對數據對象加鎖時,還需要約定一些規則,例如應何時申請X鎖或S鎖、持鎖時間、何時釋放等。我們稱這些規則爲封鎖協議(Locking Protocol)。對封鎖方式規定不同的規則,就形成了各種不同的封鎖協議。下面介紹三級封鎖協議。三級封鎖協議分別在不同程度上解決了丟失的修改、不可重複讀和讀"髒"數據等不一致性問題,爲併發操作的正確調度提供一定的保證。下面只給出三級封鎖協議的定義,不再做過多探討。
1級封鎖協議

1級封鎖協議是:事務T在修改數據R之前必須先對其加X鎖,直到事務結束才釋放。事務結束包括正常結束(COMMIT)和非正常結束(ROLLBACK)。1級封鎖協議可防止丟失修改,並保證事務T是可恢復的。在1級封鎖協議中,如果僅僅是讀數據不對其進行修改,是不需要加鎖的,所以它不能保證可重複讀和不讀"髒"數據。
2級封鎖協議

2級封鎖協議是:1級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,讀完後即可釋放S鎖。2級封鎖協議除防止了丟失修改,還可進一步防止讀"髒"數據。
3級封鎖協議

3級封鎖協議是:1級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,直到事務結束才釋放。3級封鎖協議除防止了丟失修改和不讀'髒'數據外,還進一步防止了不可重複讀。
事務隔離級別

儘管數據庫理論對併發一致性問題提供了完善的解決機制,但讓程序員自己去控制如何加鎖以及加鎖、解鎖的時機顯然是很困難的事情。索性絕大多數數據庫以及開發工具都提供了事務隔離級別,讓用戶以一種更輕鬆的方式處理併發一致性問題。常見的事務隔離級別包括:ReadUnCommitted、ReadCommitted、RepeatableRead和Serializable四種。不同的隔離級別下對數據庫的訪問方式以及數據庫的返回結果有可能是不同的。我們將通過幾個實驗深入瞭解事務隔離級別以及SQL Server在後臺是如何將它們轉換成鎖的。
 Serializable

Serializable隔離級別是最高的事務隔離級別,在此隔離級別下,不會出現讀髒數據、不可重複讀和幻影讀的問題。在詳細說明爲什麼之前首先讓我們看看什麼是幻影讀。

所謂幻影讀是指:事務1按一定條件從數據庫中讀取某些數據記錄後,事務2插入了一些符合事務1檢索條件的新記錄,當事務1再次按相同條件讀取數據時,發現多了一些記錄。

repeatable read

1:所有的select在第一次一致讀以後在事務中都會使用一樣的數據狀態快照。

2:update,delete都會使用間隙鎖來保證數據的安全。防止phantom。

3:這是採用最廣的事務隔離級別,也是mysql默認的事務隔離級別。


read commited

1:每一個select都會使用各自的數據狀態的快照。

2:如果當前的數據狀態已更新到最新,但是當單個select的時候仍然會產生不一致的數據狀態。

3:更少的間隙鎖意味着更少的死鎖。

4:唯一key的檢查在第二索引和其它外鍵檢查的時候也會產生間隙所。(gap必須被鎖定以防止在parent row被刪除後仍在child row中插入相關數據)。

5:這種隔離級別也是使用的非常普遍的隔離級別尤其是在5.1以後的版本中。

6:徵對在5.0更早的版本中,可以通過innodb_locks_unsafe_for_binlog移除gap locking。

(In V5.1, most gap-locking is removed w/ this level, but you MUST use row-based logging/replication。)


read uncommitted

1:這種隔離級別幾乎不被使用,在select將會看到各種奇怪的數據現象,當然包括其它事務還未提交的數據。

2:強烈不推薦,不能保證數據的一致性。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章