1. 程式人生 > >分散式系統原理(6)日誌技術

分散式系統原理(6)日誌技術

日誌技術

日誌技術是宕機恢復的主要技術之一。日誌技術最初使用在資料庫系統中。嚴格來說日誌技術不是一種分散式系統的技術,但在分散式系統的實踐中,卻廣泛使用了日誌技術做宕機恢復,甚至如 BigTable 等系統將日誌儲存到一個分散式系統中進一步增強了系統容錯能力

本章首先簡單介紹資料庫系統中的日誌技術,進而抽象簡化問題模型,在簡化模型的基礎上介紹兩種實用的日誌技術 Redo Log 與 No Redo/No undo Log

資料庫系統日誌技術簡述

在資料庫系統中實現宕機恢復,其難點在於資料庫操作需要滿足 ACID,尤其在支援事務(transaction)的資料庫系統中宕機往往發生在某些事務只執行了部分操作的時候。此時宕機恢復的主要目標就是資料庫系統恢復到一個穩定可靠狀態,消除未完成的事務對資料庫狀態的影響。資料庫的日誌主要分為 Undo Log、Redo Log、Redo/Undo Log 與 No Redo/No Undo Log。這四類日誌的區別在更新日誌檔案和資料檔案的時間點要求不同,從而造成效能和效率也不相同。本文不就資料庫中的這四類日誌技術做深入討論,相關資訊可以參考有關資料庫系統方面的資料

Redo Log 與 Check point

問題模型

首先簡化原資料庫系統中的問題模型為一個較為簡單的模型:假設需要設計一個高速的單機查詢系統,將資料全部存放在記憶體中以實現高速的資料查詢,每次更新操作更新一小部分資料(例如key-value 中的某一個 key)。現在問題為利用日誌技術實現該記憶體查詢系統的宕機恢復。與資料庫的事務不同的是,這個問題模型中的每個成功的更新操作都會生效。這也等效為資料庫的每個事務只有一個更新操作,且每次更新操作都可以也必須立即提交(Auto commit)

Redo Log

Redo Log 是一種非常簡單實用的日誌技術。在上節的問題模型中,只需按如下流程更新既可以實用 Redo Log
Redo Log 更新流程:

1.將更新操作的結果(例如 Set  K1=1,則記錄 K1=1)以追加寫(append)的方式寫入磁碟的
日誌檔案
2.按更新操作修改記憶體中的資料
3.返回更新成功

上述更新流程中第 2 步沒有考慮修改記憶體資料需要多執行緒互斥等問題,但對於說明 Redo Log的原理沒有影響。從 Redo Log 的流程可以看出, Redo 寫入日誌的是更新操作完成後的結果(雖然本文不討論 Undo Log,這點是與 Undo Log 的區別之一),且由於是順序追加寫日誌檔案,在磁碟等對順序寫有力的儲存裝置上效率較高用 Redo Log 進行宕機恢復非常簡單,只需要“回放"日誌即可
Redo Log 的宕機恢復:

1.從頭讀取日誌檔案中的每次更新操作的結果,用這些結果修改記憶體中的資料

從 Redo Log 的宕機恢復流程也可以看出,只有寫入日誌檔案的更新結果才能在宕機後恢復。這也是為什麼在 Redo Log 流程中需要先更新日誌檔案再更新記憶體中的資料的原因。假如先更新記憶體中的資料,那麼使用者立刻就能讀到更新後的資料,一旦在完成記憶體修改與寫入日誌之間發生宕機,那麼最後一次更新操作無法恢復,但之前使用者可能已經讀取到了更新後的資料,從而引起不一致的問題

Check point

宕機恢復流量的缺點是需要回放所有 redo 日誌,效率較低,假如需要恢復的操作非常多,那麼這個宕機恢復過程將非常漫長。解決這一問題的方法即引入 check point 技術。在簡化的模型下, check point 技術的過程即將記憶體中的資料以某種易於重新載入的資料組織方式完整的 dump 到磁碟,從而
check point:

1.向日志文件中記錄“Begin Check Point”
2.將記憶體中的資料以某種易於重新載入的資料組織方式 dump 到磁碟上
3.向日志文件中記錄“End Check Point”

在 check point 流程中,資料可以繼續按照上面的redo流程被更新, 這段過程中新更新的資料可以 dump到磁碟也可以不 dump 到磁碟,具體取決於實現。例如,check point 開始時 k1=v1,check point 過程中某次更新為 k1 = v2,那麼 dump 到磁碟上的 k1 的值可以是 v1 也可以是 v2
基於 check point 的宕機恢復流程:

1.將 dump 到磁碟的資料載入到記憶體。
2.從後向前掃描日誌檔案,尋找最後一個“End Check Point”日誌
3.從最後一個“End Check Point”日誌向前找到最近的一個“Begin Check Point”日誌,並回
放該日誌之後的所有更新操作日誌

上述 check point 的方式依賴 redo 日誌中記錄的都是更新後的資料結果這一特徵,所以即使 dump的資料已經包含了某些操作的結果,重新回放這些操作的日誌也不會造成資料錯誤。同一條日誌可以重複回放的操作即所謂具有“冪等性”的操作。工程中,有些時候 Redo 日誌無法具有冪等性,例如加法操作、append 操作等。此時,dump 的記憶體資料一定不能包括“begin check point”日誌之後的操作。為此,有兩種方法,其一是 check point 的過程中停更新服務,不再進行新的操作,另一種方法是,設計一種支援快照(snapshot)的記憶體資料結構,可以快速的將記憶體生成快照,然後寫入check point 日誌再 dump 快照資料。至於如何設計支援快照的記憶體資料結構,方式也很多,例如假設記憶體資料結構維護 key-value 值,那麼可以使用雜湊表的資料結構,當做快照時,新建一個雜湊表接收新的更新,原雜湊表用於 dump 資料,此時記憶體中存在兩個雜湊表,查詢資料時查詢兩個雜湊表併合並結果

No Undo/No Redo log

本節介紹另一種特殊的日誌技術“No Undo/No Redo log”,這種技術也稱之為“0/1 目錄”(0/1 directory)
本節介紹這種技術並不再使用上節的問題場景,而假設另一種問題場景:若資料維護在磁碟中,
某批更新由若干個更新操作組成,這些更新操作需要原子生效,即要麼同時生效,要麼都不生效

0/1 目錄技術中有兩個目錄結構,稱為目錄 0(Directory 0)和目錄 1(Directory 1)。另有一個結構稱為主記錄(Master record)記錄當前正在使用的目錄稱為活動目錄。主記錄中要麼記錄使用目錄 0,要麼記錄使用目錄 1。目錄 0 或目錄 1 中記錄了各個資料的在日誌檔案中的位置

下圖給出了一個 0/1 目錄的例子。活動目錄為目錄 1,資料有 A、B、C 三項。查目錄 1 可得A、B、C 三項的值分別為 2、5、2
0/1目錄
0/1 目錄的資料更新過程始終在非活動目錄上進行,只是在資料生效前,將主記錄中的 0、1 值反轉,從而切換主記錄
0/1 目錄資料更新流程:

1.將活動目錄完整拷貝到非活動目錄
2.對於每個更新操作,新建一個日誌項紀錄操作後的值,並在非活動目錄中將相應資料的位置
修改為新建的日誌項的位置
3.原子性修改主記錄:反轉主記錄中的值,使得非活動目錄生效

0/1 目錄的更新流程非常簡單,通過 0、1 目錄的主記錄切換使得一批修改的生效是原子的。0/1 目錄將批量事務操作的原子性通過目錄手段歸結到主記錄的原子切換。由於多條記錄的原子修改一般較難實現而單條記錄的原子修改往往可以實現,從而降低了問題實現的難度。在工程中0/1 目錄的思想運用非常廣泛,其形式也不侷限在上述流程中,可以是記憶體中的兩個資料結構來回切換,也可以是磁碟上的兩個檔案目錄來回生效切換

工程投影

日誌技術的使用非常廣泛,在 zookeeper 系統中,為了實現高效的資料訪問,資料完全儲存在記憶體中,但更新操作的日誌不斷持久化到磁碟,另一方面,為了實現較快速度的宕機恢復, zookeeper週期性的將記憶體資料以 checkpoint 的方式 dump 到磁碟

MySQL 的主從庫設計也是基於日誌。從庫只需通過回放主庫的日誌,就可以實現與主庫的同步。由於從庫同步的速度與主庫更新的速度沒有強約束,這種方式只能實現最終一致性

Mola與 Armor系統支援多種不同的儲存引擎,對於接受到的更新操作,這兩個系統將操作日誌(redo log)儲存到磁碟,引擎可以通過回放日誌實現副本資料的同步。在 mola中,由於不需要強一致性,日誌與資料分離,且日誌也儲存多個副本,當日志副本更新滿足 quorum 要求後就返回。引擎通過回放日誌的方式實現資料更新,由於回放速度不一致,mola 提供最終一致性保證。同時,由於返回使用者更新成功時只保證日誌更新成功,此時讀取引擎資料未必可以讀到最新更新的資料。Armor*中更新了這一設計,日誌與資料不分離,更新日誌的同時也更新引擎資料,從而可以立刻讀取到成功更新的資料