經典分散式論文閱讀:Bayou
本文是Bayou論文的閱讀筆記,Bayou是一個多副本弱一致性儲存系統。Bayou主要在弱連線的移動計算環境下,給協作應用提供管理衝突的並行活動。在弱連線的網路環境下,其實也只能實現弱一致性副本。在Bayou中,客戶端能夠在任何情況下讀取資料,即使這些資料中的衝突沒有完全解決。本文的主要貢獻為:
- 引入在更新資料時依賴檢查和合並過程進行應用定義的衝突檢測和消解;
- 定義了更新的兩種狀態:提交和試探;
- 如何管理兩種狀態的更新;
- 副本如何走向最終一致;
- 如何在Bayou中保證安全性(筆記略)。
Bayou應用
- 會議室排程:會議室排程程式允許使用者預定會議室。如果使用者申請的會議室在指定時間有衝突,那麼會重新安排到備選的時間段。
- 文獻資料庫:文獻資料庫允許使用者協同維護一個文獻資料庫,維護一個識別符號到文獻條目的對映,如果存在衝突的標籤,那麼將衝突的標籤自動重新命名。
Bayou基本系統模型
Bayou系統的資料集合會在一定數量的伺服器上儲存副本,應用程式通過API和伺服器互動。API提供了兩種操作:讀和寫。Bayou使用了弱一致性副本模型,提供了任意讀寫訪問。客戶端會訪問不同的伺服器進行讀寫,需要使用Session Guanrantees來保證客戶端觀察到的一致性。
為了實現應用層面的衝入檢測和消解,更新操作需要提供用於檢測和消解衝突需要的額外資訊。伺服器之間會使用逆熵協議相互交換記錄的寫入操作,只要伺服器之間不存在永久的分裂,那麼寫入操作會最終到達全部的伺服器。
衝突檢測和消解
支援應用定義的衝突檢測和消解是Bayou設計的重點,衝突檢測和消解的機制分別是依賴檢查和合併過程。
- 寫入操作的執行過程:
Bayou_Write (update,dependency_check,mergeproc) {
IF (DB_Eval (dependency_check.query) <> dependency_check.expected_result)
resolved_update = Interpret (mergeproc);
ELSE
resolved_update = update;
DB_Apply (resolved_update);
}
複製程式碼
- 一個寫入操作:
Bayou_Write(
update = {insert,Meetings,12/18/95,1:30pm,60min,“Budget Meeting”},dependency_check = {
query = “SELECT key FROM Meetings WHERE day = 12/18/95
AND start < 2:30pm AND end > 1:30pm”,expected_result = EMPTY},mergeproc = {
alternates = {{12/18/95,3:00pm},{12/19/95,9:30am}};
newupdate = {};
FOREACH a IN alternates {
# check if there would be a conflict
IF (NOT EMPTY (
SELECT key FROM Meetings WHERE day = a.date
AND start < a.time + 60min AND end > a.time))
CONTINUE;
# no conflict,can schedule meeting at that time
newupdate = {insert,a.date,a.time,“Budget Meeting”};
BREAK;
}
IF (newupdate = {}) # no alternate is acceptable
newupdate = {insert,ErrorLog,“Budget Meeting”};
RETURN newupdate;}
)
複製程式碼
依賴檢查
如果伺服器在當前資料上的查詢沒有返回想要的結果,那麼就意味著產生了衝突。Bayou的依賴檢查可以檢測寫入-寫入衝突和讀取-寫入衝突,因為依賴檢查可以做任意查詢,因此可以實現資料上高度靈活的約束。
合併過程
當檢測到衝突之後,伺服器會執行合併過程。當衝突無法自動合併時,衝突會被記錄到日誌中,從而讓人工去消除衝突。
副本一致性
Bayou實現的上最終一致性,主要通過兩條原則保證:
- 寫入操作在所有伺服器上有一個一致的順序;
- 衝突檢測和合並過程都是確定的,因此每個伺服器都會以同樣的方式來消解衝突。
Bayou伺服器接收到的寫入請求最初是試探狀態,最終才變成提交狀態。每個寫入操作都會打上標記<時間戳,標記的伺服器ID>
,伺服器使用的是邏輯時鐘,通常和真實時間同步,但是在伺服器從逆熵過程中收到寫入操作後,需要前推它的時鐘。當伺服器通過逆熵協議收到新的寫入之後,需要通過撤銷寫入來重新按順序應用寫入操作。
寫入穩定性和提交
當寫入操作被伺服器最後一次執行之後,寫入操作認為進入穩定狀態,意味著伺服器已經收到了在這個寫入之前的所有寫入操作。
檢查寫入是否穩定可以通過逆熵協議完成,伺服器交換用於最新寫入操作的時間戳,當一個寫入操作時間戳低於全部伺服器時間戳時,那麼這個寫入是穩定的。對於每個資料,選定一個主伺服器,讓其他伺服器通過它交換提交的寫入。
儲存系統實現
儲存系統由三部分組成:寫入日誌、元祖儲存和撤銷日誌。
- 寫入日誌:包含排好序的伺服器收到的寫入請求;
- 元祖儲存:包含當前寫入的執行結果;
- 撤銷日誌:用來撤銷寫入操作。
當寫入變為穩定之後,請求就可以在日誌刪去。元祖儲存採用記憶體關係型資料庫實現,儲存了提交和全部兩個版本的資料庫,它們資料儲存在一起,使用兩個位元組來標記提交或者全部。撤銷日誌能夠撤銷之前寫入元祖儲存的更改。為了實現故障恢復,需要儲存寫入日誌和元祖儲存檢查點到穩定儲存中。
- 將寫入應用到資料庫
Receive_Writes (writeset,received_from) {
IF (received_from = CLIENT) {
# Received one write from the client,insert at end of WriteLog
# first increment the server’s timestamp
logicalclock = MAX(systemclock,logicalclock + 1);
write = First(writeset);
write.WID = {logicalclock,myServerID};
write.state = TENTATIVE;
WriteLog_Append(write);
Bayou_Write(write.update,write.dependency_check,write.mergeproc);
} ELSE {
# Set of writes received from another server during anti-entropy,
# therefore writeset is ordered
write = First(writeset);
insertionPoint = WriteLog_IdentifyInsertionPoint(write.WID);
TupleStore_RollbackTo(insertionPoint);
WriteLog_Insert(writeset);
# Now roll forward
FOREACH write IN WriteLog AFTER insertionPoint DO
Bayou_Write(write.update,write.mergeproc);
# Maintain the logical clocks of servers close
write = Last(writeset);
logicalclock = MAX(logicalclock,write.WID.timestamp);
}
}
複製程式碼
參考文獻
- Terry,Douglas B.,et al. "Managing update conflicts in Bayou,a weakly connected replicated storage system." SOSP. Vol. 95. 1995.