1. 程式人生 > >數據庫事務隔離級別和鎖實現機制

數據庫事務隔離級別和鎖實現機制

約定 表鎖 四種 back 數據庫操作 升級 數據對象 三級封鎖 pro

1. 數據庫事務處理中出現的數據不一致的情況

在多個事務並發做數據庫操作的時候,如果沒有有效的避免機制,就會出現種種問題。大體上有四種問題,歸結如下:

1.1 丟失更新

如果兩個事務都要更新數據庫一個字段X,x=100

事務A 事務B
讀取X=100 讀取X=100
寫入x=X+100 寫入x=X+200
事務結束x=200 事務結束x=300
最後x=300

兩個不同事物同時獲得相同數據,然後在各自事務中同時修改了該數據,那麽先提交的事務更新會被後提交事務的更新給覆蓋掉,這種情況事務A的更新就被覆蓋掉了、丟失了。

1.2 臟讀(未提交讀)

防止一個事務讀到另一個事務還沒有提交的記錄。 如:

事務A 事務B
寫入x=X+100 (x=200)
讀取X=200 (讀取了事務B未提交的數據)
事務回滾x=100
事務結束x=100
事務結束

事務讀取了未提交的數據,事務B的回滾,導致了事務A的數據不一致,導致了事務A的臟讀 !

1.3 不可重復讀

一個事務在自己沒有更新數據庫數據的情況,同一個查詢操作執行兩次或多次的結果應該是一致的;如果不一致,就說明為不可重復讀。
還是用上面的例子

事務A 事務B
讀取X=100 讀取X=100
讀取X=100 寫入x=X+100
事務結束, x=200
讀取X=200(此時,在同一個事務A中,讀取的X值發生了變化!)
事務結束

這種情況事務A多次讀取x的結果出現了不一致,即為不可重復讀 。

1.4 幻讀(Phantom Read)

事務A讀的時候讀出了15條記錄,事務B在事務A執行的過程中 增加 了1條,事務A再讀的時候就變成了 16 條,這種情況就叫做幻影讀。
不可重復讀說明了做數據庫讀操作的時候可能會出現的問題。

2. 事務隔離級別通過鎖的實現機制

兩個鎖:
排他鎖 被加鎖的對象只能被持有鎖的事務讀取和修改,其他事務無法在該對象上加其他鎖,也不能讀取和修改該對象
共享鎖 被加鎖的對象可以被持鎖事務讀取,但是不能被修改,其他事務也可以在上面再加共享鎖。

特別的,對共享鎖: 如果兩個事務對同一個資源上了共享鎖,事務A 想更新該數據,那麽它必須等待 事務B 釋放其共享鎖。

在運用 排他鎖 和 共享鎖 對數據對象加鎖時,還需要約定一些規則,例如何時申請 排他鎖 或 共享鎖、持鎖時間、何時釋放等。稱這些規則為封鎖協議(Locking Protocol)。對封鎖方式規定不同的規則,就形成了各種不同的封鎖協議。

2.1 一級封鎖協議 (對應 read uncommited)   

一級封鎖協議是:事務 在對需要修改的數據上面(就是在發生修改的瞬間) 對其加共享鎖(其他事務不能更改,但是可以讀取-導致“臟讀”),直到事務結束才釋放。事務結束包括正常結束(COMMIT)和非正常結束(ROLLBACK)。
一級封鎖協議不能避免 丟失更新,臟讀,不可重復讀,幻讀!

2.2、二級封鎖協議 (對應read commited) 

二級封鎖協議是:
1)事務 在對 需要更新的數據 上(就是發生更新的瞬間) 加 排他鎖直到事務結束, 防止其他事務讀取未提交的數據,這樣,也就避免了 “臟讀” 的情況。
2)事務 對當前 被讀取的數據 上面加 共享鎖 (當讀到時加上共享鎖),** 一旦讀完該行,立即 釋放該 該行的共享鎖** - 從數據庫的底層實現更深入的來理解,既是,數據庫會對遊標當前的數據上加 共享鎖 , 但是當遊標離開當前行的時候,立即釋放該行的共享鎖。 

二級封鎖協議除防止了“臟讀”數據,但是不能避免 丟失更新,不可重復讀,幻讀 。

但在二級封鎖協議中,由於讀完數據後 立即 釋放共享鎖,所以它不能避免可重復讀 ,同時它也不能避免 丟失更新 ,如果事務A、B同時獲取資源X,然後事務A先發起更新記錄X,那麽 事務B 將等待事務 A 執行完成,然後獲得記錄X 的排他鎖,進行更改。這樣事務 A 的更新將會被丟失。 具體情況如下:

事務A 事務B
讀取X=100(同時上共享鎖) 讀取X=100(同時上共享鎖)
讀取成功(釋放共享鎖) 讀取成功(釋放共享鎖)
UPDATE X=X+100 (上排他鎖)
UPDATING A(等待事務A釋放對X的排他鎖)
事務成功(釋放排他鎖)X=200
UPDATE X=X+200(成功上排他鎖)
事務成功(釋放排他鎖)X=300

由此可以看到,事務A的提交被事務B覆蓋了,所以不能防止 丟失更新。

如果要避免 丟失更新,我們需要額外的操作, 對凡是讀到的數據加 共享鎖排他鎖 ,這個往往需要程序員自己編程實現,比如在Oracle 中,需要加 SELECT FOR UPDATE 語句,表明,凡是該事務讀到的數據,額外的加上排他鎖,防止其他數據同一時間獲取相同數據,這樣就防止了 丟失更新

2.3、三級封鎖協議 (對應reapetable read )

三級封鎖協議是:二級封鎖協議加上事務 在讀取數據的瞬間 必須先對其加 共享鎖 ,但是 直到事務結束才釋放(與二級區別) ,這樣保證了可重復讀(既是其他的事務職能讀取該數據,但是不能更新該數據)。

三級封鎖協議除防止了“臟”數據 和不可重復讀 。但是這種情況不能避免 幻讀 和 丟失更新 的情況,在事務 A 沒有完成之前,事務 B 可以新增數據,那麽 當事務 A 再次讀取的時候,事務B 新增的數據會被讀取到,這樣,在該封鎖協議下,幻讀 就產生了。 如果事務A 和 事務B 同時讀取了資源X=100,同樣,如果事務A先對X進行 更新X=X+100,等待事務A執行完成X=200,那麽事務B 獲得X的排他鎖,進行更新 X=X+200,然後提交 X=300,同樣A的更新被B所覆蓋!( 如果要避免 丟失更新,我們需要額外的操作, 對凡是讀到的數據加 共享鎖排他鎖 ,這個往往需要程序員自己編程實現,比如在Oracle 中,需要加 SELECT FOR UPDATE 語句,表明,凡是讀到的數據,我會加 排他鎖,防止其他數據同一時間獲取相同數據 ) !

進階:repeatable read 導致死鎖的情況(即便是 不同的資源在相同的順序下獲取 )。 比如 事務1 讀取 A,同時 事務2 也讀取 A,那麽事務1和事務2 同時對 A 上了共享鎖,然後事務1 要UPDATE A,而此時 事務2 也要 UPDATE A,這個時候 事務1 等待 事務2 釋放其在 A 上的共享鎖,然後 事務2 要等待 事務1 釋放其在 A 上的共享鎖,這樣,事務1 和 事務2 相互等待,產生死鎖!(SQL Server/DB2 裏面有 UPDATE LOCK 可以解決這種情況,具體的思路是,在 repeatable read 的情況下,將讀取的數據 上的 UPDATE 鎖,介於 共享鎖 和 排他鎖之間的一種鎖,該鎖的作用是 當出現上面這種情況後,事務1 和 事務2 對 A 上的是 UPDATE 鎖,那麽誰先 要修改 A,那麽該事務就會將 UPDATE 鎖可以順利升級為 排他鎖對該數據進行修改!)

2.4、最強封鎖協議(對應Serialization)

四級封鎖協議是對三級封鎖協議的增強,其實現機制也最為簡單,直接對 事務中 所 讀取 或者 更改的數據所在的表加表鎖, 也就是說,其他事務不能 讀寫 該表中的任何數據。這樣所有的 臟讀,不可重復讀,幻讀 ,都得以避免!

數據庫事務隔離級別和鎖實現機制