段恢復與寫前日誌Segment-Based Recovery Write-ahead logging revisited

2021202210011 王佔坤

摘要

本論文重新討論了寫前日誌,然後去掉了兩個核心假設:

  • 頁面是恢復單元
  • 時間戳(lns)應該存儲在每個頁面上。

恢復單個應用程序級對象(而不是頁面)簡化了對對象大小與頁面大小不同的系統的處理。我們將展示如何在頁面上消除對lsn的需要,從而爲大型對象啓用DMA或零複製I/O,增加併發性,並減少應用程序、緩衝區管理器和日誌管理器之間的通信。

我們的實驗表明,鬆散耦合顯著降低了組件之間的延遲影響。這使得該方法特別適用於大型分佈式系統,並支持分佈式系統和事務存儲的思想的“交叉傳播”。然而,這些優勢是有代價的:段與physiological redo的不相容阻止了一些重要的優化。我們展示了分配如何使(或防止)ARIES頁(和physiological redo)與段混合。我們提出了一個分配策略,以避免使其他ARIES和LSN-free頁面組合複雜化的不希望的交互,然後證明這兩種方法和我們的組合是正確的。

引言

作者在重新審視寫前日誌之後,移除了兩個基本假設:

  • 頁面是數據恢復的基本單位
  • 每個頁面都有一個日誌序列號(log-sequence number, LSN)

頁面對於空間管理和作爲與磁盤的傳輸單元仍然很有用。頁面和段可以很好地一起工作(就像在體系結構中一樣),在我們的例子中,可以保持與傳統的面向頁面的數據結構(如b -樹)的兼容性。我們的第二個貢獻是展示如何使用基於段的恢復來消除頁面上對lsn的需求。無lsn頁面方便了多頁面對象,並且通過隱式設置頁面時間戳,允許我們對相同頁面的更新重新排序,並利用更高級別的併發性。

然而,基於段的重做僅限於盲寫(blind write):不檢查它們修改的頁面的操作。通常,盲寫要麼將一個範圍置零,要麼以偏移量寫一個字節數組。相反,ARIES重做檢查磁盤上頁面的內容並支持生理重做(physiological redo)。生理重做假設每個頁面在內部是一致的,並在每個頁面上存儲標題(header)。這允許系統重新組織頁面,然後回寫更新,而不生成日誌條目。這對於經常在頁面中合併空間的b樹尤其重要。此外,通過仔細安排頁面回寫,生理操作可以在不記錄更新的情況下重新平衡b -樹節點。

寫前日誌

steal/no-force恢復

  • no-force
    事務不需要在提交時就寫回。因爲redo日誌可以在恢復期間對丟失的頁面進行重建
  • steal
    緩衝區管理器可以寫出髒頁面,因爲undo日誌可以對被覆蓋的數據進行重建

基於頁面的恢復

基於頁面的恢復方式的核心是每個頁面都是自一致性的(self-consistent)並且每個頁面都使用LSN(log-sequence number)進行標識。LSN可以保證在恢復階段redo日誌中的每個條目只執行一次。

我們提出了一種類似於ARIES的方法,但是它在應用程序數據的粒度上工作。我們將這種恢復單元稱爲段:一組可以跨越頁面邊界的字節。我們還提出了基於段的恢復和允許兩者共存的ARIES的泛化。將段邊界與高級原語對齊可簡化併發性並支持新的優化,例如針對大型對象的零複製I/O。

多頁面對象

image_63

基於頁面的恢復很難應對大對象——當一個對象的大小超過一個頁面時。因爲即使頁面是連續的,LSN也會打破這種連續性。爲了能夠在讀取和寫入時保證對象的一致性,需要非常複雜的操作。

而基於段恢復的機制可以避免每個頁面的LSN,允許將對象存儲爲一個連續的段。這將使得DMA和零拷貝I/O成爲可能。

應用與緩存交互

image_64

上圖(a)顯示了更新一個頁面上的一條記錄的順序。在傳統的數據庫中,一個頁面上只有一條記錄,因此在原子執行上述順序時,可以使頁面版本與日誌保持同步。

在上圖(b)畫出了基於頁面的恢復機制的問題。當一個頁面中同時存在兩個對象A和B,當要對A進行更新時就會產生一個日誌記錄,但是還沒有更新頁面,此時產生了一個對B的更新,同時寫入了日誌條目並更新了頁面,那麼此時LSN就是B條目的LSN。此時就意味着,比B1小的LSN的記錄的更新操作已經更新了頁面了但是實際上沒有。這就出現了衝突。發生這種問題的原因在對於同一個頁面的不同對象而言,任何一個對象的更新產生的LSN都會作用到所有對象上。這本質上是直寫緩存(write-through)。

我們希望的策略是回寫緩存(write-back):更新隻影響緩存副本。

日誌重排序

在每個頁面上都有一個LSN也使得重新排序日誌條目變得困難,即使在獨立事務之間也是如此。這會干擾對重要請求進行優先級排序的機制,並且與緩衝區管理器一樣,會將日誌與應用程序緊密耦合,從而增加同步和通信開銷。理論上,只要對象和事務(例如提交記錄)中的順序保持不變,所有獨立的日誌條目都可以重新排序。但是,一般來說,即使是兩個獨立事務中的更新也無法重新排序,因爲它們可能共享頁面。一旦將LSN分配給共享頁面上的日誌條目,獨立更新的順序就固定了。

使用面向段的恢復,我們甚至不需要在頁面更新時知道LSN,並且可以在以後選擇時分配LSN。在某些情況下,我們在將日誌寫入磁盤時分配LSN,這允許我們將高優先級條目放在日誌緩衝區的前面。

分佈式恢復

面向頁面的恢復導致應用程序、緩衝區管理器和日誌管理器之間的緊密耦合。緊密耦合在傳統的單核機器上可能很好,但在將組件分發到不同的機器時,以及在較小程度上分發到不同的核時,它會導致性能問題。面向段的恢復使組件之間的耦合更簡單、更鬆散。

基於段的恢復機制

預寫日誌系統的四個組成部分:

  • 日誌文件
    每個記錄由以下部分組成:

    • LSN 日誌中的偏移量
    • 生成該條目的事務ID
    • 條目更改的段
    • 該段是否含有LSN的Boolean
    • redo information (重做需要的所有其他信息)
  • 應用緩存

  • 緩衝區管理器

  • 頁面文件

恢復

基於分段的恢復過程分爲三步:

  • 分析檢查日誌內容並重建緩衝器管理器宕機時的內容。這允許後續的步驟忽略日誌中的某些內容。
  • 重做歷史,使系統恢復到宕機前的狀態
  • 撤消回滾不完整的事務和日誌補償記錄

基於段恢復的方法也支持steal/no-force。日誌條目執行的操作被限制在物理重做(physical redo)和邏輯撤銷(logical undo)。物理重做使得即使系統處於不一致狀態也能夠進行,邏輯撤銷則是用於保持事務的一致性。邏輯撤銷允許事務在底層數據更改後安全回滾,例如當另一個事務的b -樹插入重新平衡了一個節點時。

段頁式混合機制

混合策略下的log結構:

LSN 事務ID 修改的段 LSN標誌位 preimage postimage
  • LSN標誌位
    用於表示該記錄是屬於頁式的還是段式的。LSN爲1時表示爲頁式的。

算法

image_66

上圖主要描述了段s的三個lsn的更新過程。

  • s->lsn_stable
    修改了內存中的頁面的第一個LSN
  • s->lsn_volatile
    the latest such value
  • log_stable
    寫入磁盤的最近的log的LSN

image_67

上圖展示了在混合模式下如何進行redo操作。

image_68

上圖展示瞭如何併發更新N個段。

恢復不變性

段和對象

本文使用對象這個術語來指代不考慮數據庫其餘部分內容而寫入的數據。每個對象在物理上都由一組段支持:原子記錄、任意長度的磁盤區域。段使用機器原語存儲;我們假設硬件能夠獨立地更新段,也許可以使用額外的機制。與ARIES類似,基於段的存儲基於多級恢復[33],這將對象嵌入了一個嵌套結構;可以展開嵌套以找到所有的段。

用s表示一個地址或者一個地址集合或者一個段,用l表示一個log條目的LSN。那麼\(s_l\)就是將log的前綴應用於s的初始值之後的段的值:

\[s_l=log_l(log_{l-1}(...(log_1(s_0)))) \]

在時刻t,如果段在緩衝區管理中,那麼就用\(s_t^{mem}\)表示,否則就使用\(\perp\)表示;如果段在硬盤上則使用\(s_t^{stable}\)表示。如果\(s_t^{mem}=s_t^{stable} || s_t^{mem}==\perp\),那麼我們就說s是乾淨的段(clean),否則就是髒的(dirty)。
\(s_t^{current}\)是保存在s中的值,那麼有:

\[s_t^{current} = \left\{ \begin{aligned} s_t^{mem} & & {if s_t^{mem}\neq\perp}\\ s_t^{stable} & & {otherwise} \end{aligned} \right. \]

具有相干緩衝區管理器的系統保持\(s_t^{current}=s_{l(t)}\)的不變性,其中\(l(t)\)是時間t最近的日誌條目的LSN。非相干系統允許\(s_t^{current}\)過時,並保持較弱的不變性:\(\exist l' \le l(t): s_t^{current} = s_{l'}\)

儘管一個頁面擁有多個對象,但是如果它們被原子更新,那麼恢復它們將會視之爲一個段/對象。否則,我們將把它們視爲若干個段的數組,每個字節就是一個段。

Coherency vs. Consistency

我們定義一個集合:

\[LSN(O) = {l: O_l = O} \]

來表示所有\(O_l\)等於某個版本\(O\)的對象的LSN。

基於頁面的存儲系統的每個頁面s都有一個LSN,s.lsn,並且有\(s.lsn \in LSN(s)\),因爲頁面的LSN總是被設置爲最近的log條目的LSN。如果s不是一個頁面,或者並不顯示地包含LSN,那麼就有\(s.lsn = \perp\)

一個對象O是損壞的,如果他是在之前的操作中從未出現的段或者這個段包含一個損壞的對象:

\[\exist segment \quad s \in O: \forall LSN l, s \neq s_l \]

當對象O處於forward操作期間(可能是事務中間)產生的狀態時,它是一致的:

\[\exist LSN \quad l: \forall object \quad o \in O, l \ in LSN(o) \]

引理1:\(O\)是一致性的當且僅當它不是撕裂的(is not torn)。

如果一個對象在沒有對該對象進行任何正在進行的修改時生成的LSN上是一致的,則該對象是一致的。與對象一樣,修改也是嵌套的;如果某些子操作尚未完成,則修改正在進行中。作爲特例;事務是對數據庫的操作;當沒有正在進行的事務時,ACID數據庫是一致的。

日誌和頁文件

日誌條目由LSN,e.lsn唯一確定,並指向一個特定對象的操作。日誌條目與事務e.tid相關聯,這是一組應該以原子的、持久的方式應用到數據庫的操作。日誌的狀態還包括三個特殊的LSN:

  • \(log_t^{trunc}\)
    存儲在硬盤上的序列的開始
  • \(log_t^{stable}\)
    存儲在硬盤上的最後一個條目
  • \(log_t^{volatile}\)
    內存中最近的條目

Write-ahead與檢查點

Write-ahead確保更新在到達頁面文件之前到達日誌文件。
日誌截斷和檢查點確保所有當前信息可以從磁盤重建。

三階段恢復

  • 分析檢查日誌內容並重建緩衝器管理器宕機時的內容。這允許後續的步驟忽略日誌中的某些內容。
  • 重做歷史,使系統恢復到宕機前的狀態
  • 撤消回滾不完整的事務和日誌補償記錄

事務回滾

爲了支持回滾,我們爲每個更高級別的對象更新記錄一個邏輯撤消(logical undo),爲每個段更新記錄一個物理撤消(physical undo)。每一次高級撤銷的註冊都會使低級邏輯和物理撤銷失效,事務提交也是如此。無效的撤銷操作將被視爲不再存在。

分配

分配主要有兩種方法。第一種方法通過適當放置數據和避免重用最近釋放的資源來避免不安全的衝突。第二種方法確定何時將數據寫入日誌,確保系統中某處存在由正在進行的事務釋放的數據副本。
如下圖所示是四種分配策略。
image_65

前兩種策略記錄預映像,導致額外的日誌記錄成本;第三個選項(標記爲“XOR”)指的是將新值作爲舊值的函數存儲的任何差異日誌記錄策略;第四個等待重用空間,直到釋放空間的事務提交。這使得它不適用於釋放空間以便立即重用的索引和事務。

差分日誌記錄被提出作爲增加主內存數據庫併發性的一種方法,並且必須精確地應用一次日誌條目,但是順序是任意的。相反,我們的方法避免了一次要求,並且仍然能夠並行化重做(儘管在較小的範圍內)。日誌預映像允許其他事務覆蓋舊對象所佔用的空間。這可能是由於頁面壓縮造成的,它將頁面上的空閒空間合併到單個區域中。因此,對於支持重組的頁面,在回收時記錄預映像是最簡單的方法。對於邊界不變的整個頁面或段,不會出現頁面壓縮之類的問題,因此沒有什麼理由在回收時進行日誌記錄;相反,事務可以在重用所釋放的空間之前記錄預映像,或者完全避免記錄預映像。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章