事務併發控制
一、四類併發問題及四種事務隔離級別
首先介紹四類併發問題:
1.更新丟失:兩個事務都同時更新一行資料,一個事務對資料的更新把另一個事務對資料的更新覆蓋了。這是因為系統沒有執行任何的鎖操作,因此併發事務並沒有被隔離開來。
2.髒讀:一個事務讀到另一個事務未提交的更新資料(A和B事務併發執行,B事務執行更新後,A事務查詢B事務沒有提交的資料,B事務回滾,則A事務得到的資料不是資料庫中的真實資料。也就是髒資料,即和資料庫中不一致的資料)。
3.不可重複讀:一個事務讀到另一個事務已提交的更新資料(A和B事務併發執行,A事務查詢資料,然後B事務更新該資料,A再次查詢該資料時,發現該資料變化了)。
4.幻讀:一個事務讀到另一個事務已提交的新插入的資料(A和B事務併發執行,A事務查詢資料,B事務插入或者刪除資料,A事務再次查詢發現結果集中有以前沒有的資料或者以前有的資料消失了)。
相應的有四種事務隔離級別供使用者選用:
更新丟失 | 髒讀 | 不可重複讀 | 幻讀 | |
---|---|---|---|---|
未提交讀 | N | Y | Y | Y |
已讀提交 | N | N | Y | Y |
可重複讀 | N | N | N | Y |
序列化 | N | N | N | N |
二、兩階段封鎖協議(2PL)及解決死鎖的方法
下面介紹兩階段封鎖協議(2PL)。2PL可以使事務的隔離級別達到可序列化。
2PL要求每個事務分兩個階段提出加鎖和解鎖申請:
1.增長階段:事務可以獲得鎖,但是不能釋放鎖
2.縮減階段:事務可以釋放鎖,但是不能獲得鎖
一開始,事務處於增長階段,事務根據需要獲得鎖。一旦該事務釋放鎖,就進入縮減階段,不能再發出加鎖請求。我們可以證明2PL協議保證衝突可序列化。對於任何事務,在排程中該事務獲得其最後的加鎖位置(增長階段結束點)稱為事務的封鎖點。這樣,多個事務可以根據它們的封鎖點進行排序,實際上,這個順序就是事務的一個可序列化順序。
但是,2PL不保證不發生死鎖,例如:
T3 | T4 |
---|---|
lock-X(B) read(B) B:=B-50 write(B) |
|
lock-S(A) read(A) lock-S(B) |
|
lock-X(A) |
*其中lock-X表示排它鎖,lock-S表示共享鎖
此時,事務T3等待T4釋放A上的共享鎖,T4等待T3釋放B上的排他鎖,發生死鎖。
死鎖的解決方法有兩種,一種是預防,一種是檢測與恢復。這裡採用第二種方法,鎖的等待關係可以用有向圖來表示,如T3->T4表示T3等待T4釋放鎖。轉化為圖後,如果有向圖中有環,則有死鎖。這裡使用NyaDB的死鎖檢測與恢復方法:
1.設定一個時間戳stamp = 0;
2.為每個點設定一個”訪問戳”, visStamp, 初始化為-1;
3.任意選擇一個visStamp為-1的點, 設它為s;
4.stamp++;
5.從s開始遍歷, 對每個遍歷到的點u進行下面的判斷:
6.===> 如果visStamp[u] == -1, 則visStamp[u] = stamp, 繼續遍歷;
7.===> 如果visStamp[u] != -1 and visStamp[u] < stamp, 跳過點u, 即不以它為節點繼續向下遍歷;
8.===> 如果visStamp[u] == stamp, 則有環, 結束演算法;
9.當從s開始遍歷完且並沒發行環, 重複3), 直到沒有visStamp為-1的點.
10.當整個演算法結束時, 都沒發現環, 則無環.
將等待圖維護在記憶體中, 每當出現等待時, 則向圖中加入一條邊。 每向圖中加入一條邊, 便進行一次死鎖檢測。 如果加入某條邊後檢測到了死鎖, 則撤銷加入這條邊的事務。
三、新的問題—級聯回滾
考慮如下圖的事務排程:
T5 | T6 | T7 |
---|---|---|
lock-X(A) read(A) lock-S(B) read(B) write(A) unlock(A) |
||
lock-X(A) read(A) write(A) unlock(A) |
||
lock-S(A) read(A) |
如果在事務T7的read(A)步驟之後事務T5發生故障需要回滾,此時雖然T6和T7並沒有發生故障,但是因為這兩個事務中有一部分命令是在事務T5開始到故障點之間執行的,所以也需要回滾,這就導致了級聯回滾。
級聯回滾需要撤銷大量的工作,這是資料庫系統不希望見到的。可以通過將2PL協議變為嚴格2PL協議來避免級聯回滾。嚴格2PL協議不僅要求封鎖是兩階段的,還要求事務持有的所有排他鎖必須在事務提交之後才能釋放。這個要求保證了未提交事務所寫的任何資料在該事務提交之前均以排他鎖方式加鎖,防止了其他事務讀取這些資料。
應用嚴格2PL協議的級聯回滾事務排程變成了下圖的樣子,防止了級聯回滾的出現:
T5 | T6 | T7 |
---|---|---|
T5 begins lock-X(A) read(A) lock-S(B) read(B) write(A) unlock(A) ………… release all lock T5 is finished |
||
T6 begins lock-X(A) read(A) write(A) unlock(A) ………… release all lock T6 is finished |
||
T7 begins lock-S(A) read(A) ………… release all lock T7 is finished |