openGauss儲存技術(一)——行儲存引擎

Gauss松鼠會發表於2022-11-09

OLTP(聯機事務處理)系統以高併發讀寫為主,資料實時性要求非常高,資料以行的形式組織,最適合面向外存設計的行儲存引擎。隨著記憶體逐漸變大,伺服器上萬億位元組(TB)大小的記憶體已經很常見,記憶體引擎面向大記憶體而設計,提高系統的吞吐量和降低業務時延。OLAP(聯機分析處理)系統主要面向大資料量分析場景,對資料儲存效率、複雜計算效率的要求非常高。列儲存引擎可以提供很高的壓縮比,同時面向列的計算,CPU 指令快取記憶體和資料快取記憶體的命中率比較高,計算效能比較好,按需讀取列資料,大大減少不必要的磁碟讀取,非常適合資料分析場景。openGauss整個系統設計是可插拔、自組裝的,並支援多個儲存引擎來滿足不同場景的業務訴求,目前支援行儲存引擎、列儲存引擎和記憶體引擎。

openGauss儲存概覽

早期計算機程式透過檔案系統管理資料,到了20世紀60年代這種方式就開始不能滿足資料管理要求了,使用者逐漸對資料併發寫入的完整性、高效的檢索提出更高的要求。由於機械磁碟的隨機讀寫效能問題,從20世紀80年代開始,大多數資料庫一直圍繞著減少隨機讀寫磁碟進行設計。主要思路是把對資料頁面的隨機寫盤轉化為對WAL(Write Ahead Log,預寫式日誌)的順序寫盤,WAL持久化完成,事務就算提交成功,資料頁面非同步將資料重新整理到磁碟上。但是隨著記憶體容量變大和保電記憶體、非易失性記憶體的發展,以及SSD(SolidStateDisk,固態硬碟)技術的逐漸成熟,磁碟的IO(輸入輸出)效能得到極大提高,經歷了幾十年發展的儲存引擎需要調整架構來發揮SSD的效能和充分利用大記憶體計算的優勢。隨著網際網路、移動網際網路的發展,資料量劇增,業務場景呈現多樣化,一套固定不變的儲存引擎不可能滿足所有應用場景的訴求。因此現在的 DBMS需要設計支援多種儲存引擎,根據業務場景來選擇合適的儲存模型。

1.資料庫儲存引擎要解決的問題

資料庫儲存引擎要解決的問題如下:

  • 儲存的資料必須要保證原子性(A)、一致性©、隔離性(I)、永續性(D)。
  • 支援高併發讀寫,高效能。
  • 充分發揮硬體的效能,解決資料的高效儲存和檢索能力。

2.openGauss儲存引擎概述

openGauss整個系統設計是可插拔、自組裝的,支援多個儲存引擎以滿足不同場景的業務訴求。當前openGauss儲存引擎有以下3種:

  • 行儲存引擎,主要面向 OLTP場景設計,例如訂貨、發貨、銀行交易系統。
  • 列儲存引擎,主要面向 OLAP場景設計,例如資料統計報表分析。
  • 記憶體引擎,主要面向極致效能場景設計,例如銀行風控場景。

建立表的時候可以指定為行儲存引擎表、列儲存引擎表、記憶體引擎表,支援一個事務中包含對三種引擎表的 DML操作,可以保證事務的 ACID性質。

本文主要介紹openGauss行儲存引擎,其他的儲存引擎將在後面的文章進行介紹。

openGauss行儲存引擎

openGauss行儲存引擎採用原地更新(in-place update)設計,支援 MVCC(Multi- Version Concurrency Control,多版本併發控制),同時支援本地儲存和儲存與計算分離的部署方式。行儲存引擎的特點是支援高併發讀寫,時延小,適合 OLTP交易類業務場景。

(一)行儲存引擎總體架構

openGauss的行儲存引擎在設計上支援 MVCC,採用集中式垃圾版本回收機制,可以提供 OLTP業務系統的高併發讀寫要求,支援儲存、計算分離架構,儲存層非同步回放日誌。行儲存引擎架構如圖1所示。
在這裡插入圖片描述

圖1 行儲存引擎架構

圖注:資料頁面快取池中快取資料頁面,在資料頁面中存放元組以及元組的歷史版本並集中管理,使用 Vacuum(垃圾清理)執行緒進行定期的空間回收。

行儲存引擎的關鍵技術有:

  • 基於事務ID以及ctid(行號)的多版本管理。
  • 基於 CSN(CommitSequenceNumber,待提交事務的序列號,它是一個64位遞增無符號數)的多版本可見性判斷以及 MVCC機制。頁面,在資料頁面中存放元組以及元組的歷史版本並集中管理,使用Vacuum(垃圾清理)執行緒進行定期的空間回收。
  • 基於大記憶體設計的緩衝區管理。
  • 平滑無效能波動的增量檢查點(checkpoint)。
  • 基於並行回放的快速故障例項恢復。

主要模組如圖2所示。
在這裡插入圖片描述

圖2 行儲存主要模組

(二)行儲存的基本模型與頁面組織結構

行儲存的元組結構以及頁面組織,是行儲存 DML實現、可見性判斷以及行儲存各種功能與管理機制的基石。

由於行儲存是基於磁碟的儲存引擎,因此儲存格式的設計遵從段頁式設計,儲存結構需要以頁面(page)為單位,方便與作業系統核心以及檔案系統的介面進行互動。也是由於這個原因,頁面的大小需要和目標系統中一個 block(塊)的大小對齊。在比較通用的 Linux核心中,頁面大小一般預設為8192位元組(8KB)。一個基本的 Heap (堆)頁面如圖3所示。
在這裡插入圖片描述

圖3 Heap頁面示意圖

頁面開頭的位置為整個頁面的頭部資訊,記錄了這個頁面的公用資訊以及一些關鍵標識。line_pointer被 放 置 於 Header後 面,並 向 頁 面 尾 部 擴 展。line_pointer為 指 向Tuple實際資料的一個指標,類似於行指標(sentinel)的作用。

這裡需要一提的是,每個 Tuple在系統中的唯一標識ItemPointer,也被稱為ctid,儲存的是這一行所在的頁面號(block number)以及其對應的line_pointer的偏移量(offset),即這個頁面中第幾個line_pointer。這樣由一個系統內記錄的ctid,可以快速定位到這個Tuple的line_pointer,也就可以根據line_pointer的指標快速定位到Tuple的實際資料。

line_pointer的必要性也可以比較容易地總結出來。由於 Tuple的資料內容本身可以是變長的,因此如果需要找到一個在頁面中間的 Tuple,則需要按序遍歷頁面結構;而line_pointer結構本身為定長,因此可以直接以常數的複雜度找到資料所在記憶體位置。line_pointer sentinel的效果也十分明顯:line_pointer的存在使得 Tuple的對應改動侷限於頁面內部,而保持全域性標識ctid不發生變化;如果沒line_pointer,行更新需要連帶更新的元資訊、索引以及系統各處資訊,複雜度就不言而喻了。

被line_pointer指向的行記錄本身,則是從頁面結尾開始向頁面頭部延展,這樣避免在頁面填充過程中可能出現的資料移動以及空間浪費。

頁面頭部的 Header中儲存瞭如下資訊:

  • pd_lsn為最後一次改動此頁面事務寫下的 WAL[系統中一般稱為事務日誌 (transactIon log),簡稱xlog]的下一位,被xlog機制以及檢查點機制所使用。
  • pd_checksum 為頁面中的checksum,為了檢查頁面的完整性和一致性使用。
  • pd_flags是此頁面的標識位,可以讓上層透過對此頁面進行處理的介面快速識別此頁面的一些特徵,比如頁面是否有空行,頁面是否寫滿,頁面是否已經對所有事務全部可見,頁面是否被壓縮等。
  • pd_lower和pd_upper是指向頁面空閒空間起止的指標,即pd_lower指向下一個line_pointer的位置,而pd_upper指向下一個行記錄資料填充的位置,這樣既可以快速進行頁面的填充修改,也可以方便計算頁面的空閒空間。
  • pd_special指標用於記錄一些特殊的儲存管理方式以及介面所需的記憶體區域。
  • pd_prune_xid記錄上一次對此頁面進行清理的xid(事務ID,事務號)。
  • pd_xid_base以及pd_multi_base為這個頁面上xid的base基準,即該頁面上所有的記錄的xid都由頁面自身記錄的 xid(32位)與 base(64位)計算得到,是64位xid的實現方式。

每個記錄(上文元組的資料部分)是資料庫中最基本的資料儲存單位,其自身的結構以及記錄的資訊也是系統中資料儲存方式、DML、事務 ACID 特性的關鍵。資料部分結構如圖4所示。
在這裡插入圖片描述

圖4 資料部分結構
  • xmin是最初始的事務ID(Transaction ID,簡稱xid),即插入此條記錄的事務ID。
  • xmax是刪除或更新此條記錄的xid。如果此記錄未被更改或刪除,那麼xmax為0。
  • t_cid記錄的是命令ID(Command ID),命令ID 用於一個事務內部多步操作的一種記錄與跟蹤。
  • t_ctid記錄了此條記錄的ctid值,或者是更新版本的ctid值。這個會在後面展開 DML時講到。
  • 兩個t-infomask是事務以及儲存資料狀態的標識位,用於快速判斷。xmin、xmax兩個事務ID,結合其對映的 Clog(提交日誌)和 CSN Log,一同構成了可見性判斷的核心關鍵要素。

(三)行儲存的多版本管理以及 DML操作

openGauss行儲存的多版本機制與業界比較常見的關聯式資料庫有較大的不同,核心區別為行儲存的多版本在更新的時候並不是就地更新,而是在原有頁面中保留上一個版本,轉而在這個頁面(如果空間不夠會在新頁面中)中建立一個新的版本進行歷史版本的累積與更新。

相應的頁面中會同時存有不同版本的同一行資料,拿到不同快照的事務,在讀寫這些不同版本時互不衝突,有著很好的併發效能。對歷史版本的檢索可以在頁面本身或鄰近頁面進行,也不需要額外的CPU開銷以及IO 開銷,有著非常高的效率。同時,事務管理以及持久化角度也變得非常清晰簡潔,省去了類似於就地更新所需要的記錄、執行以及持久化的 Undo(回退)等相關操作。

以下就以一個 DML的例子簡單介紹行儲存結構以及 MVCC的實現。

假設我們在一個xid為10的事務中,在一個只有一列varchar(變長字串型別) 資料的表中插入一條資料 ‘A’,該行資料存入編號為0的資料頁面上,則該行儲存結構如圖5所示。

在這裡插入圖片描述

圖5 行儲存結構示意圖1

可以看到xmax為0,此時該記錄為有效記錄。

假設在此基礎上,在事務xid=20中做了刪除此行的操作,則此記錄的行儲存結構如圖6所示。
在這裡插入圖片描述

圖6 行儲存結構示意圖2

此時xmax被標記為20,如果此事務提交,那麼此行最終會被回收。

如果在之前插入(insert)操作的基礎上,在事務 xid=30 中連續對該行做兩次更新。

第一次更新的行儲存結構如圖7所示。
在這裡插入圖片描述

圖7 行儲存結構示意圖3

原有行失效,透過t-ctid記錄新版本的ctid值,進而指向下一行。

第二次更新的行儲存結構如圖8所示。
在這裡插入圖片描述

圖8 行儲存結構示意圖4

第二個版本也變為歷史版本,透過ctid指向最新版本,不過值得注意的是,第二個版本的xmin、xmax都為30,即此版本在同一事務中被刪除,而最新版本 xmin仍為30,只是t-cid從0增加為1[假設此事務連續執行了兩次更新(update)操作]

更新後的頁面如圖9所示。
在這裡插入圖片描述

圖9 行儲存結構示意圖5

以上幾個簡單的例子比較直觀地展示了行儲存的基本儲存結構、行儲存的 DML以及行儲存的 MVCC是如何結合在一起共同作用的。

儲存引擎內部,索引也是重要的組成部分,索引本身指向儲存的是key(鍵)到ctid(行號)的對映。上面也提到過了,ctid實際上指向的是line_pointer的檢索資訊,因此索引的頁面上儲存的資訊及其與資料頁面的關係如圖10所示。
在這裡插入圖片描述

圖10 索引的頁面上儲存的資訊及其與資料頁面的關係

當然,可能會出現更新操作的新版本無法放入舊版本所在頁面的情況,這種情況下頁面和索引情況的對比如圖11所示。

在這裡插入圖片描述

圖11 新版本無法放入舊版本所在頁面時的頁面和索引情況對比

此種情況下,索引會有兩條記錄(entry),兩條記錄代表了 key對應新舊版本的ctid,這樣方便從索引直接跨頁面進行搜尋。

(四)基於 CSN的 MVCC機制

openGauss採用行級MVCC機制,歷史版本集中儲存,垃圾清理代價低。每個事務有一個單獨的事務狀態儲存區域,記錄了該事務的狀態資訊和 CSN。CSN 在openGauss內部使用一個全域性自增的長整數作為邏輯的時間戳,模擬資料庫內部的時序。

例如,如圖12所示,圖中每個非只讀事務在執行過程中會取得一個 xid(事務號),在事務提交時會推進 CSN,同時會將當前 CSN 與事務的xid對映關係儲存起來。

在這裡插入圖片描述

圖12 CSN-xid對映

因此當一個事務拿到的快照為 CSN=3時,事務 TX2、TX4、TX6、TX7、TX8的CSN 分別為4、6、5、7、8,對於該事務的快照而言,這幾個事務的修改都不可見。

MVCC解決的是讀寫併發衝突問題。更新資料的時候,原地更新,把老版本放到歷史版本區頁面裡,同時維護新版本元組到老元組的指標。讀元組的時候,根據快照Snapshot.CSN 來判斷應該讀到哪個版本。

資料庫在執行SQL的時候,首先會獲取一個快照時間戳Snapshot,當掃描資料頁面的時候,根據Snapshot.CSN 和事務狀態來判斷哪個元組版本可見或者都不可見。主要分以下3種場景:

  • 元組的事務狀態區中是回滾狀態或者執行中狀態,不可見。
  • 元組的事務狀態區中是提交狀態,如果 Snapshot.CSN 比事務區裡的CSN小,當前元組不可見,讀取前一個版本繼續比較 CSN。反之可見。
  • 元組的事務狀態區中是待提交狀態,需要等待提交。CSN 本身與xid也會留存一個對映關係,以便將事務本身及其對應的可見性進行關聯,這個對映關係會留存在 CSN Log中,如圖13所示。
    在這裡插入圖片描述
圖13 CSN Log中對映關係

此對映機制類似於 Clog本身,只不過不同的是,Clog記錄的是事務ID 的相關執行狀態(執行中/提交/回滾),如圖14所示。
在這裡插入圖片描述

圖14 Clog記錄的事務ID的相關執行狀態

進一步結合前面講過的行頭的結構(其中的 xmin、xmax)、Clog以及上述 CSNLog的對映機制,MVCC的大致判斷流程如圖15所示。
在這裡插入圖片描述

圖15 MVCC判斷流程

簡單地總結如下:

  • 如果當前事務ID小於一行的xmin,那麼就需要檢索xmin對應的 Clog,讀取此事務狀態,以此來判斷此行資料是否對當前事務可見。
  • 如果當前事務ID 大於一行中的 xmax,那麼說明此行資料的更新/刪除發生於本事務開始之前,此行資料對本事務一定不可見(但不排除此行資料的新版本對本事務可見,因為新舊版本是單獨進行判斷的)。
  • 如果xid落在了xmin、xmax中間,就需要依據 CSN 來判斷本事務的快照下對應資料是否應該被看到,需要檢索 CSN Log來進行對比判斷。

(五)行儲存的空間回收

透過上面所介紹的行儲存的多版本併發控制機制,可以發現由於更新和刪除並不實際在頁面中刪除頁面本身,資料庫長時間執行後,會有大量的歷史版本殘存在儲存空間中,造成了空間的膨脹。為了解決這一問題,儲存引擎內部需要定期對歷史資料進行清理,以保證資料庫的健康執行。

行儲存對於儲存空間的清理存在於多個層面,有多種方式。其中在頁面一級的機制,稱為heap_page_prune。顧名思義,就是在頁面內部進行空間的清理。這種清理模式能夠比較好地解決更新多版本帶來的同一個資料記錄關聯的長長的歷史版本堆疊、標記刪除的記錄以及無效的記錄。這種pruning(空間回收)的手段在對頁面進行讀取的過程中由頁面的空閒空間閾值觸發,僅改動 heap頁面本身,不對索引頁面進行改動。因此heap_page_prune是一種較為輕量化的清理方式。舉例如下:

如有一個記錄a,被前後更新,導致同時有6個歷史版本儲存於兩個不同的頁面中,如圖16所示。
在這裡插入圖片描述

圖16 記錄a的6個歷史版本

頁面級別的自我清理效果為圖17所示。

在這裡插入圖片描述

圖17 頁面級別的自我清理

可以看到,清理過程中分別對頁面1和頁面2中的內容進行了回收,但是由於之前的跨頁面導致的兩個索引記錄指向不同頁面,卻被保留了下來。

在頁面級別的清理之外,還有表級別、資料庫級別的整體清理,這個機制稱之為Vacuum 操作。Vacuum 操作在整個資料庫級別進行廢舊元組的清理,同時也會清理索引。Vacuum 操作可以由資料庫使用者對資料庫或資料庫內物件主動調起,同時資料庫後臺也會有工作執行緒在滿足閾值時或者定期進行資料庫自動的 Vacuum 操作,如圖18所示。
在這裡插入圖片描述

圖18 Vacuum 操作

Vacuum 自身除了清理空間外,也順帶承擔了更新統計資訊的功能,以便最佳化器能更準確地進行代價估算。

在 Vacuum 操作過程中,還會對整個資料庫級別都可見的元組進行freeze操作。舉例來說,當一個元組被插入並提交,而後續沒有更新操作,資料庫系統上也不再有早於這個提交的事務時間點,需要對這條元組做可見性判斷的事務,此時認為此元組就可以被任何人看見了,那麼其相關的事務ID 就可以被轉化為一個特殊的事務ID——Freeze xid,以表示這種狀態。當 Vacuum 操作清理整個系統時,系統中最小活躍事務之前的提交日誌(Clog),也同上面說到的,不再被需要,因此 Vacuum 操作也會對這部分提交日誌進行清理和回收。

當然,Vacuum 操作本身是一個相對高成本的操作,因此,每個表檔案會有一個對應的可見性對映(visibility map),來記錄這個表資料檔案中對應的頁面是否已經處於全部可見狀態,這種情況下 Vacuum 操作在執行過程中就可以跳過這部分頁面,節省開銷。由於一般系統中儲存的絕大部分資料都不與當前活躍事務相關,因此此最佳化可以大大提升 Vacuum 操作的效率。

(六) 行儲存的共享快取管理

前面提到,行儲存是一個基於磁碟的儲存引擎。為了避免磁碟的IO 的高昂開銷,儲存引擎會快取一部分頁面在記憶體中,便於隨時對其進行檢索和更改。儲存引擎會對快取的頁面進行篩選、替換和淘汰,保證留存在快取的頁面能夠提高整個引擎的執行效率。

行儲存中也有著種類較多的快取,除去正常資料頁面的快取之外,還存在用於快取各類表的元資訊的資料表快取(relationcache),以及用於加速資料庫系統資訊以及系統表操作的系統表快取(catalogcache)。這些種類的快取都以頁面的形式歸共享緩衝區結構管理。

共享緩衝區由大量的頁面槽位構成,槽位本身有對應的描述結構體,以及用於管理處於這個操作的併發操作的頁面級別鎖,並配有一個空閒連結串列來進行空閒空間管理,如圖19所示。
在這裡插入圖片描述

圖19 共享緩衝區

行儲存引擎中操作對事務的讀寫請求,都會先傳遞至共享緩衝區。對一個頁面的請求會先在緩衝區內進行搜尋,如果未命中,則獲取一個空的槽位(可能需要淘汰掉已經在緩衝區中不常用的頁面),再與檔案系統進行互動將所需頁面讀到槽位中,加鎖並使用。根據業務的特徵和負載及共享緩衝區的大小,已經在緩衝區內的資料頁面會被反覆命中,避免了與磁碟的IO 開銷,從而加速整個事務處理流程。

對頁面的更改也會放在快取中並被標為髒頁面。此時後臺寫執行緒(background圖19 共享緩衝區writer)會定期對髒頁面進行清理和刷盤操作,把空間返還給緩衝區。另一方面,檢查點操作在進行時也會將所有的頁面刷盤,確保資料的持久化。這裡需要注意的一個概念是,當一個事務提交後,這個事務執行過程中更改的頁面並不一定被刷盤至磁碟,事務本身的持久化機制實際上是由事務強制刷盤的 WAL,也就是xlog來保證的。在檢查點操作後,因為相關頁面都已經持久化至磁碟,因此檢查點操作時間點之前的xlog, 就可以被回收了。這個機制會在後續的章節繼續展開。

共享緩衝區實際上是記憶體與持久化儲存中協調管理排程的核心機制,對資料庫管理系統的效率有著很大的影響。為了進一步提升緩衝區中頁面的命中率,一些可能會影響緩衝區內頁面與業務關聯性的操作,都會使用一個專門單獨開闢的緩衝區,即環狀緩衝區(ringbuffer)。批次的讀/寫及 Vacuum 頁面清理,都屬於這類操作。

(七)並行日誌系統設計

資料庫的日誌系統非常關鍵,它是資料持久化的關鍵保證。傳統資料庫一般都採用序列刷日誌的設計,因為日誌有順序依賴關係。例如:一個由事務產生的 Redo/Undo日誌是有前後依賴關係的。openGauss的日誌系統採用多個Log Writer(日誌寫盤)執行緒並行寫的機制,充分發揮SSD的多通道IO 能力,如圖20所示。
在這裡插入圖片描述

圖20 並行刷日誌示意圖

關鍵設計如下:

  • 整個事務的 WAL日誌不能拆分到多個事務日誌共享緩衝區,必須寫到一個事務日誌共享緩衝區。
  • 故障恢復 WAL,並行恢復,必須按照 LSN(日誌序列號)大小順序恢復。
  • 每個事務結束前需要保證對應的事務日誌 LSN 已經刷盤完成。
  • 事務分配事務日誌共享緩衝區考慮 NUMA 架構適配。

(八)持久化及故障恢復系統設計

資料庫的日誌系統非常關鍵,它是資料持久化的關鍵保證。基於事務ID 的多版本管理及歷史版本的累積及清理方式,行儲存引擎主要以 Redo日誌(也就是上文提到的 XLog)作為主要的持久化手段,配以增量的檢查點及日誌的並行回放,支援資料庫例項的快速故障恢復。

1.事務的Redo日誌機制

Redo日誌在事務對資料進行修改時產生,用來記錄事務修改後的資料或是事務對資料做 的 具 體 操 作。比 如,簡 單 的INSERT/UPDATE/DELETE 操 作 會 產 生 如圖21所示的 Redo日誌。
在這裡插入圖片描述

圖21 Redo日誌

一些非事務直接修改的關鍵操作也會記錄到 Redo日誌,比如新頁面的申請、顯式的事務提交、檢查點等。記錄 Redo日誌的原則,就是在資料庫發生故障後,可以從最後一個檢查點開始,透過 Redo日誌的回放,恢復到與資料庫例項發生故障前的狀態一致。

Redo日誌除了應用於資料恢復、資料備份與還原以及資料庫主備例項之間的主備同步、不同資料庫例項/叢集間的同步都需要依賴 Redo日誌的機制。為了保障資料的一致性,在事務修改的相關頁面刷盤之前,需要先把對應的 Redo日誌刷盤,也就是遵循 WAL的原則。

因為事務的提交以及操作之間的順序對於資料一致性是至關重要的,因此 Redo日誌也必須將此順序記錄下來。每條 Redo日誌都配有一個日誌序列號,即 Log Sequence Number(LSN)。在行儲存的系統中,LSN 為一個遞增的64位無符號整數。系統中各類機制,如檢查點及主備例項之間的同步機制、仲裁機制,都需要依靠系統中推進的LSN 或是恢復出來的 LSN 作為重要的標記或判斷依據。

2.全量與增量檢查點

在上述對事務日誌以及共享緩衝區的描述中,有一個關鍵的資訊,那就是事務日誌的持久化與事務提交是同步的,但事務內對頁面相關修改的持久化與事務提交不是同步的;也就是說,事務提交需要與這個事務相關的 Redo日誌被強制刷盤,但是並不強制要求相關的頁面也被強制刷盤。當一個資料庫例項故障重啟後,例項在啟動過程中,之前沒有能夠及時刷盤的改動需要使用事務日誌進行恢復。但是日誌回放的代價是很高的,效能也相對比較慢。為了避免每次資料庫都需要從頭恢復事務日誌,資料庫自身會定期建立檢查點,使用者也可以透過命令手動建立檢查點。

建立檢查點的過程中,儲存引擎會將資料緩衝區中髒頁寫到磁碟中,並記錄日誌檔案和控制檔案。記錄資訊中的recLSN 代表著此次檢查點中,在此 LSN 之前的日誌對應的所有改動均已被持久化,下次的資料恢復可以直接從此 LSN 開始;同時在此LSN 之前的事務日誌,在其他用途(主備例項同步、資料備份等)時,也可以被回收重新使用。

由於檢查點本身需要將緩衝區內所有的髒頁面刷盤(全量檢查點),因此每次檢查點從效能角度會對資料庫例項所在物理環境引入大量的IO,磁碟的峰值往往意味著效能的波動。同時因為存在大量的IO 開銷,因此檢查點的打點不能過於頻繁,recLSN推進較慢,那麼重啟資料庫時也就會存在較多的 Redo日誌需要回放,存在重啟恢復時間過長的問題。為了解決這一問題,行儲存引擎引入了增量檢查點的概念。

在增量檢查點機制下,會維護一個髒頁面佇列(dirtypagequeue)。髒頁是按照LSN 遞增的順序放到佇列中的,定期由一個專門刷髒頁面的後臺執行緒頁面刷盤執行緒(pagewriter)進行定期定量的刷髒頁下盤操作,髒頁面佇列如圖22所示。

在這裡插入圖片描述

圖22 髒頁面佇列

佇列中維護一個recLSN,記錄目前已經被刷盤的髒頁對應的 LSN 大小,即在佇列中髒頁對應的事務提交、其相對的事務日誌下盤後,此recLSN 標記會被更新。在觸發增量檢查點時,並不需要等待髒頁刷盤,而是可以使用當前髒頁佇列的recLSN 作為檢查點的recLSN 記錄。增量檢查點的存在使得整個系統中的IO 更加平滑,並且系統的故障恢復時間更短,可用性更高。

3.並行回放

Redo日誌的回放指的是將 Redo日誌中記錄的改動重新應用到系統/頁面中的過程,這個過程通常發生在例項故障恢復抑或是主備例項之間的資料同步過程中的備機例項上(即主例項的改動,備機例項也需要回放完成,以達到與主例項狀態一致的效果)。當前資料庫所在物理例項往往有較多的 CPU 核,而日誌回放卻往往還是單執行緒進行運作,在日誌回放的過程中資料庫例項無法充分利用物理環境資源。

為了能夠充分利用 CPU 多核的特點,顯著加快資料庫異常後恢復及備機例項日誌回放的速度,行儲存引擎採用了多執行緒並行方式回放日誌,如圖23所示。
在這裡插入圖片描述

圖23 多執行緒並行方式回放日誌

整個並行回放系統的設計採用生產者-消費者模型,分配模組負責解析、分配日誌到回放模組,回放模組負責消費、回放日誌。

為了 達 成 這 一 設 計,實 現 中 採 用 了 帶 阻 塞 功 能 的 無 鎖 SPSC(Single Producer Single Consumer)佇列。分配執行緒作為生產者將解析後的日誌放入回放執行緒的列隊中,回放執行緒從佇列中消費日誌進行回放。無鎖SPSC佇列如圖24所示。
在這裡插入圖片描述

圖24 無鎖SPSC佇列

為了提升整體並行回放機制的可靠性,會在對一個頁面的回放動作中,對事務日誌中的 LSN 和頁面結構中的last_LSN[詳見前面章節中描述的 HeapPageHeader(堆頁面頭)結構體]進行校驗,以保證回放過程中資料庫系統的一致性。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69997967/viewspace-2922534/,如需轉載,請註明出處,否則將追究法律責任。

相關文章