全部產品
Search
文件中心

PolarDB:WAL日誌並行回放

更新時間:Jul 06, 2024

本文介紹了PolarDB PostgreSQL版的WAL日誌並行回放功能。

前提條件

支援的PolarDB PostgreSQL版的版本如下:

  • PostgreSQL 14(核心小版本14.5.1.0及以上)

  • PostgreSQL 11(核心小版本1.1.17及以上)

說明

您可通過如下語句查看PolarDB PostgreSQL版的核心小版本的版本號碼:

  • PostgreSQL 14

    select version();
  • PostgreSQL 11

    show polar_version;

背景資訊

PolarDB PostgreSQL版的一寫多讀架構下,唯讀節點(Replica 節點)運行過程中,LogIndex後台回放進程(LogIndex Background Worker)和會話進程(Backend)分別使用LogIndex資料在不同的Buffer上回放WAL日誌,本質上達到了一種並行回放WAL日誌的效果。

鑒於WAL日誌回放在PolarDB叢集的高可用中起到至關重要的作用,將並行回放WAL日誌的方法用到常規的日誌回放路徑上,是一種很好的最佳化思路。

並行回放WAL日誌至少可以在以下三個情境下發揮優勢:

  • 主庫節點、唯讀節點以及備庫節點崩潰恢複(Crash Recovery)的過程。

  • 唯讀節點LogIndex BGW進程持續回放WAL日誌的過程。

  • 備庫節點Startup進程持續回放WAL日誌的過程。

術語

  • Block:資料區塊。

  • WAL:Write-Ahead Logging,預寫記錄檔。

  • Task Node:並存執行架構中的子任務執行節點,可以接收並執行一個子任務。

  • Task Tag:子任務的分類標識,同一類的子任務執行順序有先後關係。

  • Hold List:並存執行架構中,每個子進程調度執行回放子任務所使用的鏈表。

原理介紹

  • 概述

    一條WAL日誌可能修改多個資料區塊Block,因此可以使用如下定義來表示WAL日誌的回放過程:

    1. 假設第i條WAL日誌LSN為LSNi,其修改了m個資料區塊,則定義第i條WAL日誌修改的資料區塊列表Blocki​=[Blocki,0​,Blocki,1​,...,Blocki,m​]

    2. 定義最小的回放子任務為Taski,j​=LSNi​−>Blocki,j,表示在資料區塊Blocki,j上回放第i條WAL日誌。

    3. 因此,一條修改了m個Block的WAL日誌就可以表示成m個回放子任務的集合:TASKi,∗​=[Taski,0​,Taski,1​,...,Taski,m​]

    4. 進而,多條WAL日誌就可以表示成一系列回放子任務的集合:TASK∗,∗​=[Task0,∗​,Task1,∗​,...,TaskN,∗​]

    在日誌回放子任務集合Task∗,∗中,每個子任務的執行,有時並不依賴於前序子任務的執行結果。

    假設回放子任務集合如下:TASK∗,∗​=[Task0,∗​,Task1,∗​,Task2,∗​],其中:

    • Task0,∗​=[Task0,0​,Task0,1​,Task0,2​]

    • Task1,∗​=[Task1,0​,Task1,1​]

    • Task2,∗​=[Task2,0​]

    並且,Block0,0​=Block1,0​,Block0,1​=Block1,1​,Block0,2​=Block2,0​

    則可以並行回放的子任務集合有三個:[Task0,0​,Task1,0​]、[Task0,1​,Task1,1​]、[Task0,2​,Task2,0​]。

    綜上所述,在整個WAL日誌所表示的回放子任務集合中,存在很多子任務序列可以並存執行,而且不會影響最終回放結果的一致性。PolarDB藉助這種思想,提出了一種並行任務執行架構,並成功運用到了WAL日誌回放的過程中。

  • 並行任務執行架構

    將一段共用記憶體根據並發進程數目進行等分,每一段作為一個環形隊列,分配給一個進程。通過配置參數設定每個環形隊列的深度:共用記憶體配置

    • Dispatcher進程。

      • 通過將任務分發給指定的進程來控制並發調度。

      • 負責將進程執行完的任務從隊列中刪除。

    • 進程組。

      組內每一個進程從相應的環形隊列中擷取需要執行的任務,根據任務的狀態決定是否執行。進程組

    • 任務

      環形隊列的內容由Task Node組成,每個Task Node包含五個狀態:Idle、Running、Hold、Finished、Removed。

      • Idle:表示該Task Node未分配任務。

      • Running:表示該Task Node已經分配任務,正在等待進程執行,或已經在執行。

      • Hold:表示該Task Node有前向依賴的任務,需要等待依賴的任務執行完再執行。

      • Finished:表示進程組中的進程已經執行完該任務。

      • Removed:當Dispatcher進程發現一個任務的狀態已經為Finished,那麼該任務所有的前置依賴任務也都應該為Finished狀態,Removed狀態表示Dispatcher進程已經將該任務以及該任務所有前置任務都從管理結構體中刪除;可以通過該機制保證Dispatcher進程按順序處理有依賴關係的任務執行結果。

      任務上述狀態機器的狀態轉移過程中,黑色線標識的狀態轉移過程在Dispatcher進程中完成,橙色線標識的狀態轉移過程在並行回放進程組中完成。

    • Dispatcher進程

      Dispatcher進程有三個關鍵資料結構:Task HashMap、Task Running Queue以及Task Idle Nodes。

      • Task HashMap負責記錄Task Tag和相應的執行工作清單的hash映射關係。

        • 每個任務有一個指定的Task Tag,如果兩個任務間存在依賴關係,則它們的Task Tag相同。

        • 在分發任務時,如果一個Task Node存在前置依賴任務,則狀態標識為Hold,需等待前置任務先執行。

      • Task Running Queue負責記錄當前正在執行的任務。

      • Task Idel Nodes負責記錄進程組中不同進程,當前處於Idle狀態的Task Node。

      Dispatcher調度策略如下:

      • 如果要執行的Task Node有相同Task Tag的任務在執行,則優先將該Task Node分配到該Task Tag鏈表最後一個Task Node所在的執行進程。目的是讓有依賴關係的任務盡量被同一個進程執行,減少進程間同步的開銷。

      • 如果期望優先分配的進程隊列已滿,或者沒有相同的Task Tag在執行,則在進程組中按順序選擇一個進程,從中擷取狀態為Idle的Task Node來調度任務執行。目的是讓任務盡量平均分配到不同的進程進行執行。

      調度策略

    • 進程組

      該並存執行針對的是相同類型的任務,它們具有相同的Task Node資料結構。在進程組初始化時配置SchedContext,指定負責執行具體任務的函數指標:

      • TaskStartup:表示進程執行任務前需要進行的初始化動作。

      • TaskHandler:根據傳入的Task Node,負責執行具體的任務。

      • TaskCleanup:表示執行進程退出前需要執行的回收動作。

      進程組1

      進程組中的進程從環形隊列中擷取一個Task Node,如果Task Node當前的狀態是Hold,則將該Task Node插入到Hold List的尾部。如果Task Node的狀態為Running,則調用TaskHandler執行;如果TaskHandler執行失敗,則設定該Task Node重新執行需要等待調用的次數,預設為3,將該Task Node插入到Hold List的頭部。進程組2

      進程優先從Hold List頭部搜尋,擷取可執行檔Task。如果Task狀態為Running,且等待調用次數為0,則執行該Task;如果Task狀態為Running,但等待調用次數大於0,則將等待調用次數減去1。進程組3

  • WAL日誌並行回放

    LogIndex資料中記錄了WAL日誌和其修改的資料區塊之間的對應關係,而且LogIndex資料支援使用LSN進行檢索。因此,PolarDB資料庫在Standby節點持續回放WAL日誌過程中,引入了上述並行任務執行架構,並結合LogIndex資料將WAL日誌的回放任務並行化,提高了Standby節點資料同步的速度。

    工作流程

    • Startup進程:解析WAL日誌後,僅構建LogIndex資料而不真正回放WAL日誌。

    • LogIndex BGW後台回放進程:成為上述並行任務執行架構的Dispatcher進程,利用LSN來檢索LogIndex資料,構建日誌回放的子任務,並分配給並行回放進程組。

    • 並行回放進程組內的進程:執行日誌回放子任務,對資料區塊執行單個日誌的回放操作。

    • Backend進程:主動讀取資料區塊時,根據PageTag來檢索LogIndex資料,獲得修改該資料區塊的LSN日誌鏈表,對資料區塊執行完整日誌鏈的回放操作。工作流程

    • Dispatcher進程利用LSN來檢索LogIndex資料,按照LogIndex插入順序枚舉PageTag和對應LSN,構建{LSN -> PageTag},組成相應的Task Node。

    • PageTag作為Task Node的Task Tag。

    • 將枚舉組成的Task Node分發給並存執行架構中進程組的子進程進行回放。工作流程

使用指南

在Standby節點的postgresql.conf檔案中添加以下參數,開啟WAL日誌並行回放功能。

polar_enable_parallel_replay_standby_mode = ON