1. 程式人生 > 其它 >段恢復與寫前日誌Segment-Based Recovery Write-ahead logging revisited

段恢復與寫前日誌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。

多頁面物件

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

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

應用與快取互動

上圖(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時表示為頁式的。

演算法

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

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

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

上圖展示瞭如何併發更新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)。每一次高階撤銷的註冊都會使低階邏輯和物理撤銷失效,事務提交也是如此。無效的撤銷操作將被視為不再存在。

分配

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

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

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