數據庫恢復子系統的常見技術和方案對比(一)

作者:實驗室小陳/大數據開放實驗室

對於事務型數據庫而言,最關鍵的功能是要保證事物ACID屬性,其中原子性和持久性依靠恢復子系統保證。事務在進行中如果發現無法繼續,就需要用恢復子系統進行回滾;或者出現系統崩潰,也需要依靠恢復子系統把數據庫恢復到崩潰前狀態。在本專欄中,我們主要介紹Logging Protocols / Recovery Algorithms,它們分別是事務型數據庫恢復子系統中的兩個關鍵部分。

— Logging Schemes—

恢復子系統中的關鍵是恢復算法,目的是要實現兩個過程。首先是事務執行過程中爲系統恢復做的準備工作,目前大多數系統通常採用日誌記錄方式,儘管事務執行過程中同時記錄數據更新日誌會有額外開銷,但如果沒有日誌,一旦系統崩潰則無法實現系統恢復和未完成事務的回滾。此外,還有Shadow Paging方案,即數據每次修改都通過Copy-on-Write的方式進行。在更新數據時,複製一份原數據的副本並在副本上進行更新,最後通過用副本替換原始數據的方式完成操作。Shadow Paging方案開銷較大,一般用在更新不頻繁的場景下,如文本編輯器等類似場景,因此事務型數據庫系統裏大都採用基於日誌的方案。第二個過程是在出現系統故障或事務回滾的情況時,如何利用系統記錄的日誌信息並採用合適策略來保證數據庫能夠恢復到正確狀態。

  • Physical Logging & Logical Logging

Logging分爲Physical Logging和Logical Logging兩類。Physical Logging指在日誌記錄中記錄對數據項的修改,如數據項A在修改之前的值爲90,修改之後是100,Physical Logging會將數據項A的變化過程記錄下來。在一個數據庫系統中,Physical Logging可能是Value Logging,即記錄數據項、數據項ID、修改前/後的屬性值等信息;也可能是真正的物理Logging,即記錄磁盤頁面PageID、Offset和長度,以及修改前後的值。

另外一類是Logical Logging,不記錄執行結果,只記錄對數據修改的操作如delete / update等。較於Physical Logging根據修改前後的值進行恢復或重放,Logical Logging在重放時需要重新執行日誌中的操作,在回滾時需要執行日誌中所記錄操作的逆操作,比如插入對應的刪除等。

  • Physical Logging VS Logical Logging

兩種Logging各有優缺點。Logical Logging記錄的日誌內容較少,比如update操作,Logical Logging只需要記錄一條update語句即可,日誌記錄開銷少。缺點是在併發場景下較難實現,當同時有多個事務產生更新操作時,數據庫內部會將這些操作調度爲串行化序列執行,需要機制來保障每次回放操作的執行順序與調度產生的順序一致。所以,大多數數據庫系統採用Physical Logging來保證數據恢復的一致性,事務管理器(併發控制子系統)所產生的事務操作執行順序會以日誌的方式被記錄下來,恢復子系統根據日誌順序能夠保證每一個數據項修改的回滾和重放都按照順序嚴格執行。但目前,有一些數據庫系統依舊使用Logical Logging,如內存數據庫引擎VoltDB,這是因爲VoltDB引擎設計上沒有併發控制,每個CPU內核都順序執行所有操作,因此可以通過Logical Logging按序回放。
對於數據庫管理系統而言,要保證故障發生情況下的數據持久性和正確性,因此恢復子系統不可或缺。在事務執行過程中,需要撤銷時能夠回滾以保障原子性;但同時恢復子系統會帶來性能影響,因爲所有日誌記錄只有刷到磁盤上纔算真正落盤,即使事務所有操作全部完成,也一定要等日誌落盤後才能響應客戶端,因此Logging的性能往往成爲整個系統的性能瓶頸。

— Recovery System Optimization—

對於日誌或恢復子系統的優化,主要有兩類技術,一類是Group Commit,另一類是Early Lock Release。

  • Group Commit

Group Commit是將並行執行的一組事務日誌一起刷到磁盤,而非分事務每條日誌刷一次。日誌有單獨的日誌緩衝區,所有事務先把日誌寫進日誌緩衝區,通過設置單獨線程定時將日誌緩衝區中的內容刷進磁盤,或當日志緩衝區存儲滿時再刷到磁盤。

操作系統提供了Sync、Fsync、Fdatasync等不同寫磁盤的方式,其中Sync把數據刷到操作系統文件緩衝區時就視爲結束,隨後靠操作系統後臺進程把緩衝區的內容刷到磁盤,因此通過Sync方式刷磁盤可能會造成數據丟失。數據庫系統通常採用Fsync進行日誌落盤,當記錄真正寫到磁盤裏面時才返回。Fsync是將文件數據以及文件元數據如修改時間、文件長度等信息一起寫到磁盤;而Fdatasync跟Fsync的區別在於其只刷數據而不刷元數據。

在一些DBMS中,會混用Fsync和Fdatasync:當元數據修改不影響Logging,比如只有文件修改時間變化,這時只用Fdatasync即可;但如果操作修改了文件長度,這時就不能用Fdatasync,因爲Fdatasync並不保存元數據修改信息,在恢復時會造成內容部分缺失。由於很多DBMS在寫日誌時不是以增量方式增加日誌文件內容,而是一次性爲日誌文件分配足夠空間,在之後的寫日誌過程中日誌文件長度保持不變,所以可以用Fdatasync將日誌寫到磁盤。可以看到,Group Commit每次將一組事務使用一個系統調用寫到磁盤,合併很多事務I/O,從而降低整個系統的I/O。

  • Early Lock Release

在基於鎖機制實現併發控制時,如果前序事務的鎖沒有釋放,後面的事務只能處於等待鎖的狀態。圖中黑色部分表示正在進行的事務操作,灰色部分是等待日誌落盤的時間,雖然此時對數據不做修改,但只有等日誌刷到磁盤後才能釋放鎖。Early Lock Release是一種面向此場景提高性能的優化方法,策略是當事務中處理工作的部分做完就釋放鎖,然後再將日誌落盤,縮短下個事務等待鎖的時間,提高併發執行程度。
數據庫恢復子系統的常見技術和方案對比(一)

但這種方式同樣存在缺陷,比如第一個事務已經釋放鎖,但在日誌落盤時出現故障需要回滾,但由於此時鎖已經被下一個事務獲得,下一個事務要和上一個事務一起回滾,因此係統需要維護事務間的依賴關係。在現實中,鎖的提前釋放技術在數據庫中被廣泛使用。對於索引結構,如果對索引中的某個節點加鎖,會產生較大影響範圍,因爲一個索引葉子節點往往涉及一連串的很多數據記錄。如果對葉子節點加鎖,相關記錄都會被鎖住。因此在索引的使用上,通常會採用Early Lock Release而非兩階段封鎖協議,以縮短數據記錄被鎖住的時間。

— ARIES算法—

在基於磁盤的數據庫系統中,恢復子系統大都是基於ARIES(Algorithms for Recovery and Isolation ExploitingSemantics)算法實現。ARIES對於數據緩衝區和日誌緩衝區的管理採用Steal + No Force的管理策略(關於Steal + No Force的介紹在《內存數據庫解析與主流產品對比(一)》中有詳細提到)。在ARIES中,每條日誌會有一個順序號LSN(Log Sequence Number),如下圖中LSN 10號的日誌是事務T1寫Page 5的更新操作;20號LSN是事務T2寫Page 3的更新操作。需要注意的是,日誌中會保留有事務end記錄,標識事務已commit並返回客戶端,表示該事務所有操作已經完成。如果日誌中只有commit而沒有end,那可能意味着事務已經完成,但客戶端可能沒有收到響應。
數據庫恢復子系統的常見技術和方案對比(一)

  • ARIES三階段恢復

ARIES的恢復算法分三個階段:Analysis、Redo、Undo,每階段具體細節後面會詳細介紹。

  1. Analysis:在出現crash重啓後,系統首先會從磁盤上讀出日誌文件,分析日誌文件內容,判斷哪些事務在系統crash時處於Active狀態,以及哪些Page在出現故障時被修改過。

  2. Redo:系統在redo階段根據日誌重現故障現場,包括將內存中的Dirty Page恢復到crash時的狀態,相當於重放日誌歷史記錄(Repeating History),並將每條日誌記錄都執行一遍,包括沒有commit的事務日誌。

  3. Undo:Undo階段系統開始撤銷沒有完成的事務。上圖是日誌記錄的簡單示例,系統在LSN 60後crash,其中日誌中有事務T2 end的標記,所以T2已經提交,而事務T1和T3都尚未完成,事務T1和T3對於P1、P3和P5的修改如果已落盤,就需要從磁盤上撤銷。
  • 日誌記錄的數據結構

對於ARIES恢復子系統,恢復過程需要基於Logging所存儲的信息進行。ARIES中日誌由多條日誌記錄組成,一條日誌記錄裏包含修改數據項的事務ID、Page ID + Offset、長度、修改前後的數值以及額外的控制信息。

ARIES的日誌類型包括Update、Commit、Abort、End以及補償日誌記錄CLR(Compensation Log Record )。CLR用於預防因事務回滾過程中出現故障造成影響,當事務回滾時,每回滾一條日誌就記錄一條CLR,系統可以通過CLR判斷哪些操作已經回滾,如果不記錄CLR則可能出現操作回滾兩次的情況。

數據庫恢復子系統的常見技術和方案對比(一)
在正常記錄日誌時,ARIES會記錄redo和undo信息,記錄的日誌包含修改前後的值等。一般來說,日誌落盤是順序寫,因此數據庫在配置上會爲日誌服務單獨安排磁盤,不和存儲數據記錄的盤混用,以提升日誌寫的性能。

下面是ARIES中日誌落盤示意圖,圖中右側序列代表所有日誌,青藍色部分代表已經落盤的日誌,橙色部分表示還在日誌緩衝區裏的日誌。ARIES會記錄Flushed LSN,代表目前已有哪些緩衝區的日誌已經刷到磁盤。此外,保存數據的每個磁盤塊中都會記錄一個Page LSN,用來表示修改此數據Page的所有操作中對應的最大日誌號(即最後一個修改數據Page的操作所對應的日誌號)。在把數據緩衝區裏的數據刷到磁盤時,通過判斷Page LSN與flushed LSN的大小決定是否可以將數據刷到磁盤。如果Page LSN 小於等於Flushed LSN ,說明修改這個數據頁面的所有日誌記錄都已落盤,因此數據也可以落盤,這就是所謂的WAL(Write-Ahead-Log),日誌總是先於數據寫到磁盤。

此外,日誌記錄中還保存了Prev LSN來對應日誌所屬事務的前一個日誌號。由於在系統中所有事務共享日誌緩衝區,因此產生的日誌是穿插在一起的,可以通過Prev LSN把屬於同一個事務的所有LSN串聯起來,來找到事務所對應的所有日誌。
數據庫恢復子系統的常見技術和方案對比(一)
恢復子系統中還需要維護Xact Table和Dirty Page Table。Xact Table用來記錄所有活動的Transaction的狀態如active、commit、abort、end等;同時還記錄事務最後產生的日誌號Last LSN。Dirty Page Table用來記錄哪些數據Page從磁盤上加載到緩衝區後被修改過, 以及每個Page最早修改時的日誌號Rec LSN(即數據Page被加載到緩衝區後第一個修改操作所對應的日誌號)。

除了在日誌中記錄信息外,爲保證恢復可以成功完成,數據庫系統還需要用Master Record記錄Checkpoint的LSN,保證在每次恢復時只需要從最近的Checkpoint開始即可。由於數據庫系統在做Checkpoint時需要停機(不允許任何事務執行),這對於使用者很難接受,因此ARIES中的Checkpoint採用Fuzzy Checkpoint方式,即在進行Checkpoint時允許事務可以持續不斷執行。Fuzzy Checkpoint會產生兩個日誌記錄:Begin_Checkpoint和End_Checkpoint。Begin_Checkpoint負責記錄開始Checkpoint的時間點,End_Checkpoint記錄Xact Table和Dirty Page Table,而Checkpoint的LSN會寫到磁盤的Master Record上進行持久存儲。以上即恢復所需的全部數據結構,各類LSN的整理總結如下表所示。
數據庫恢復子系統的常見技術和方案對比(一)
數據庫恢復子系統的常見技術和方案對比(一)

— 數據庫系統的事務恢復—

  • 簡單事務恢復

對於簡單事務恢復(系統沒有出現故障,而是事務在執行過程中不再繼續),此時需要進行回滾。回滾時,系統首先從Xact Table中找出最新的LSN進行undo,隨後通過該日誌記錄的Prev LSN找到前序日誌記錄再繼續undo,直到整個事務徹底回放到開始時的狀態。和正常事務操作相似,undo時的數據實際上需要加鎖,並在回滾前會記錄補償日誌CLR。CLR會記錄undo next的LSN號以指向下一條需要undo的LSN,在undo到Transaction Start的LSN時,記錄Transaction Abort和Transaction End表明回滾結束。

  • 出現故障的事務恢復

在前文我們提到,ARIES的故障恢復分爲三個階段,下面將詳細介紹三個階段的執行細節。

1.Analysis階段

在Analysis階段,系統從磁盤上的Master Record中獲取最後一個Checkpoint日誌記錄,重構出Xact Table和Dirty Page Table,並從Begin_Checkpoint日誌記錄開始順序處理後續的日誌記錄。在遇到一個事務的end日誌時,將其從Xact Table中去除;如果遇到事務的commit日誌,則更新Xact Table中對應事務的狀態;如果遇到其它日誌記錄,判斷該事務是否在Xact Table中,不在則將其加入Xact Table,並更新Xact Table中該事務的Last LSN爲當前日誌記錄的LSN。此外,系統會判斷日誌記錄中更新的數據Page是否在Dirty Page Table,如不在則將該數據Page加入到Dirty Page Table,並將其Rec LSN設爲當前日誌號。
2.Redo階段
系統在Redo階段首先找出Dirty Page Table中所有PageRec LSN中最小的,作爲redo的起始位置,因爲再往前的日誌記錄對應的數據修改都已落盤,不會出現在Dirty Page Table中。隨後系統從redo的起始位置開始,按順序對後續更新日誌記錄(包括CLR)執行redo操作(重放)。如果遇到操作更新的Page不在Dirty Page Table中,或Page在Dirty Page Table中但Rec LSN大於當前LSN,或磁盤上的Page LSN大於當前的LSN,則都表示該LSN對應記錄已經落盤,可以直接跳過,不需要執行redo。在redo時,系統不需要再記錄日誌,因爲redo只是實現整個內存狀態的重構,如果在redo時又出現了系統故障,則按照原來操作重新進行一遍。

3.Undo階段
Undo階段目的是撤銷在系統故障時未完成的事務,開始時會建立一個需要undo的日誌集合,把每個需要回滾的事務的最後一條日誌號放入該集合中,然後開始進行循環處理。首先系統從集合裏挑出最大的LSN即最後一條進行undo,如果這條日誌是CLR補償日誌,且它的undo-next爲空,那麼說明事務已經完成undo,可以記錄一條End日誌表明事務結束;如果補償日誌的undo-next不等於空,說明還有下一條需要undo的日誌,那麼就將下一條日誌的LSN放入集合;如果是更新日誌,就回滾該日誌且記錄一條CLR日誌,然後把日誌的Prev LSN加入集合。系統會按照上述過程不斷循環,直到整個undo集合爲空。

接下來通過例子梳理一下整個過程。系統首先做了Fuzzy Checkpoint,出現了兩次更新:T1修改了P5,T2修改了P3。隨後,T1 abort 取消,LSN40記錄了補償日誌——回滾LSN10,隨後T1 End。接下來其他事務繼續進行:T3修改了P1,T2修改了P5。此時出現了Crash,該怎麼做恢復呢?首先在Analysis過程,由Checkpoint開始向後掃描,發現T1已經End不需要redo,T2、T3沒有end可以進行redo,因此Xact Table裏僅有T2 、T3,Dirty Page Table包括P1、P3、P5。在分析完成後,進行redo重做過程恢復故障現場,隨後在T2、T3進行undo每一條日誌時記錄CLR,直到undo到每個事務最初的一條。
數據庫恢復子系統的常見技術和方案對比(一)
如果在恢復的過程中又出現了Crash(如下圖所示),已經undo的兩個操作由於記錄了CLR,新redo會重做這兩個CLR,而新undo過程不會再重新回滾。恢復系統會在原有基礎上繼續進行,直到所有的事務全部undo完成。
數據庫恢復子系統的常見技術和方案對比(一)


  • ARIES小結

ARIES是一個具有成熟設計,能夠保證事務原子性和持久性的恢復系統,使用WAL和Steal + No Force緩衝區管理策略,且不影響系統正確性。ARIES中LSN是單調遞增的日誌記錄唯一標識,通過鏈接方式把一個事務的所有日誌串聯在一起。Page LSN用來記錄每個頁面最後修改操作對應的日誌號,系統通過Checkpoint減少Recovery的開銷。整個恢復分爲Analysis、Redo、Undo三個步驟,分析的目的是找出來哪些事務需要redo,哪些頁面被修改過,修改是否已經落盤;隨後通過redo恢復故障現場,利用undo將需要撤銷的事務回滾。

— 本文小結—
本文介紹了恢復系統Logging和Recovery的基本概念,並討論了傳統基於磁盤的數據庫管理系統中恢復子系統ARIES的技術原理。下一篇文章會繼續探索數據庫的恢復子系統,討論DBMS恢復中的Early Lock Release、Logical Undo,介紹兩種數據庫的恢復技術以及內存數據庫的恢復方法。

參考文獻:

  1. C. Mohan, Don Haderle, Bruce Lindsay, Hamid Pirahesh, and Peter Schwarz. 1992. ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking And Partial Rollbacks Using Write-Ahead Logging. ACM Trans. Database Syst. 17, 1 (March 1992), 94–162.

  2. Antonopoulos, P., Byrne, P., Chen, W., Diaconu, C., Kodandaramaih, R. T., Kodavalla, H., ... & Venkataramanappa, G. M. (2019). Constant time recovery in Azure SQL database. Proceedings of the VLDB Endowment, 12(12), 2143-2154.

  3. Zheng, W., Tu, S., Kohler, E., & Liskov, B. (2014). Fast databases with fast durability and recovery through multicore parallelism. In 11th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 14) (pp. 465-477).

  4. Ren, K., Diamond, T., Abadi, D. J., & Thomson, A. (2016, June). Low-overhead asynchronous checkpointing in main-memory database systems. In Proceedings of the 2016 International Conference on Management of Data (pp. 1539-1551).

  5. Kemper, A., & Neumann, T. (2011, April). HyPer: A hybrid OLTP&OLAP main memory database system based on virtual memory snapshots. In 2011 IEEE 27th International Conference on Data Engineering (pp. 195-206). IEEE.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章