資料庫系統併發控制原理
資料庫訪問是通過事務完成的,首先我們搞清楚什麼是事務?
被視為整體的一組工作
這組工作要麼完全完成,要麼全部不完成,不存在部分完成情況
真實生活中以轉賬說明事務:
第一步,從賬戶A中減去X元金額;
第二步,將X元金額存入賬戶B
這些多步操作必須全部完整完成,不能半途而廢。資料庫事務的工作方式與此相同。他們能保證,無論發生什麼事情,資料的操作處理都被看成是原子的(你永遠不會看到“轉變一半”的情況)。
原子性是DBMS維護ACID屬性的一部分,ACID包括:
Atomicity原子性 : 事務中所有操作要麼全部發生,要麼全部不。
Consistency一致性 : 資料庫從一致狀態開始到一致狀態結束。
Isolation隔離性 : 一個事務的執行是隔離於其他事務的。
Durability :如果事務已經確認提交,其效果是持久儲存在資料庫中。
那麼在併發訪問資料庫的情況下會發生什麼問題?併發流程可能會改變隔離性和一致性這兩個屬性。讓我們假設兩個使用者預訂了一架飛機的同一個座位:
客戶1 發現一個空座位
客戶2 發現同樣的空座位
客戶1預訂了這個座位
客戶2也同時預訂了這個座位
我們將事務的順序執行稱為 schedule 排程,它表示基於時間先後的一系列事務執行,在這裡客戶1和客戶2分別存在兩個事務,這個兩個事務同時發生,我們需要通過序列化 serializability 來保證事務正確執行,也就是說,需要通過一個排程來實現併發控制機制。
一個排程包含下面一些操作:
read R(X)
write W(X)
commit (所有操作完成後,這些操作應該被確認和記錄)
abort (在執行一部分操作後,如果我們退出,所有操作應該沒有一個被確認完成或記錄儲存)
為了有完整的排程,commit 或abort是被強制的。
serial schedule 序列排程是事務執行時間沒有交織,所有操作都是順序執行。
當下麵條件滿足,Conflicting operation衝突操作會出現:
它們屬於不同的事務
它們得訪問同一的物件X
至少其中一個操作是W(X) (對X的寫操作)
讓我們看看下面這些衝突操作:
寫讀衝突 Write-Read Conflict : 讀到一個未提交uncommitted的資料
讀寫衝突 Read-Write Conflict : 首次讀以後,再讀已經被修改的資料。
寫寫衝突 Write-Write Conflict :其中一個寫操作丟失
Write-Read Conflict , 也稱為 reading uncommitted data 讀未提交的資料或髒都 dirty-read ,當一個事務T2試圖讀取資料庫物件A,但是其已經被事務T1修改,還沒有提交確認,當T1繼續其事務時,物件A的資料已經不一致了,如下圖:

換句話說,髒讀是當一個事務讀取了被另外一個事務修改但是還沒有提交確認的表記錄。
讀寫衝突Read-Write Conflict , 也稱為 unrepeatable reads , ,當一個事務T1讀兩次資料庫物件A,第一讀以後事務T1等待事務T2完成,T2覆蓋重寫了物件A,當T1再次讀A時,A資料存在兩個不同版本,T1被強迫退出,因為這是不可重複的讀。

真實案例:Bob和Alice是票務員,他們要預訂一個表演票,只剩餘一張了,Alice登入進入,發現這周票比較貴,猶豫了一下,而Bob登入進入後,就立即買了這張票,然後退出。Alice決定買這張票時,發現已經沒有多餘的票了。
寫寫衝突Write-Write Conflict , 也稱為覆蓋未提交資料 overwriting uncommitted data ,它是發生在當有一個丟失修改情況下,試圖使這種場景序列只能是下面兩者之一:要麼是事務T1版本,要麼是事務T2的版本:

一旦併發事務應用到資料庫上,使用排程確保序列化,也就是執行是順序的,不會有時間的重疊,除了序列,還有以下幾種排程方式:
ACA : 避免級聯中斷
可恢復性recoverable
嚴格排程strict schedule

如果一個排程是序列化的,最好的驗證辦法是通過依賴圖。為了建立依賴圖,我們需要下面過程:
1) 每個節點每個事務的代表
2)在事務Tx寫入後還有另外的事務Ty讀嗎?如果是,從節點Tx畫線到節點Ty
3)在事務Tx讀取以後,是否有其他事務再進行寫入呢?如果是,從Tx畫線到Ty。
4)在Tx寫入後有其他事務再次寫入嗎?如果是,從Tx畫線到Ty。
如果退出事務,不要忘記移除你之前的畫線。
為了偶一個序列化的排程,依賴圖得是非迴圈的,也就是不能首尾迴圈依賴。比如下面的排程不是序列化:

而下面的排程是序列化的:

現在我們知道什麼時候一個排程是 strict嚴格 的?
當一個物件被事務T寫入,那麼一直到事務T確認提交或退出,這個物件就一直不能被再次讀取或寫入了。
那麼一個排程如何避免級聯退出呢?
當一個操作只能讀取已經被提交確認的資料時,就可以避免級聯一個個的事務退出。
那麼如何知道一個排程是可恢復的?
對於每個事務,當事務Ty讀取一些Tx事務的寫入資料時,Tx的Commit提交操作出現在Ty的Commit提交操作之前。
總結
併發控制是通過序列化方式實現,這樣能夠確保任何非序列化的執行不會發生。當然主要問題是帶來效能瓶頸。