动态

详情 返回 返回

災難恢復工具內核細節探究與分享 - 动态 详情

本文整理自 IvorySQL 2025 生態大會暨 PostgreSQL 高峯論壇的演講分享,演講嘉賓:張晨,公眾號《ZhangChen-PDU》主理人。

前言

在數據庫運維中,災難恢復始終是保障業務連續性和系統可靠性的核心環節。隨着數據庫規模和複雜性的增加,傳統工具在極端場景下的侷限性愈發明顯,因此需要更專業、高效的解決方案來應對數據損壞或不可啓動的情況。

PDU 的快速介紹

在數據庫運維場景中,災難恢復一直是考驗系統可靠性與底層機制理解的重要環節。對於 Oracle 等商業數據庫來説,已有成熟的內部工具如 DUL 可用於在數據庫無法啓動時直接從數據文件中提取數據。而在 PostgreSQL 生態中,長期缺乏類似的工具,這也正是 PDU 誕生的初衷。

PDU 是什麼

PDU(PostgreSQL Data Unloader)是專門用於 PostgreSQL 係數據庫的災難恢復工具,旨在解決數據庫無法正常啓動、單表文件損壞或歸檔丟失等極端情況下的數據恢復問題。其核心功能主要包括:

  • 從 wal 中恢復 delete/update 的原數據。
    圖片1.jpg
  • 數據庫無法啓動時直接從數據文件中提取數據。
    圖片2.png
  • 支持單表/整庫/自定義數據文件級別的恢復。
    圖片3.png
  • 提供事務級/時間區間級數據恢復。
    圖片4.png

為什麼需要 PDU

在災難恢復場景下,主流數據庫的能力存在明顯差異。

  1. Oracle 擁有成熟工具體系,如官方 DUL 與國內 ODU,可在數據庫無法啓動時直接從數據文件中恢復數據並生成可導入的 dump 文件。
  2. PostgreSQL 的 pg_filedump 工具功能有限,僅能導出已知表結構的單個數據文件,缺乏定製化能力,遇到複雜問題難以應對,且對生產環境適配不足,例如僅通過 xmax != 0 判定元組狀態,準確性有限。
  3. PG 系國產數據庫 在災備工具方面幾乎空白,多數數據庫修改了底層數據文件結構,pg_filedump 無法兼容,且缺乏專業恢復工具,在災難場景下常束手無策。

基於這一現狀,PDU 的開發旨在填補 PG 係數據庫在災難恢復中的空白。

關於 pg_filedump 更詳細的技術解析可參考👉作者往期文章

PDU 如何使用

PDU 的使用非常簡便,其軟件組成僅包含兩個部分:可執行文件與配置文件。使用步驟如下:

第一步:填入數據目錄與歸檔目錄
圖片5.png

第二步:進入 pdu 可執行文件
圖片6.png

第三步:通過簡化命令 b 執行自動初始化
圖片7.png

第四步:自由操作
圖片8.png

以下為演示圖片:

PDU 程序崩潰瞭如何反饋

PDU 採用 C 語言開發,能夠直接複用 PostgreSQL 內核的數據結構與函數,從而提高數據解析的效率與兼容性。在程序運行過程中,如果出現核心轉儲或者崩潰(core dumped),用户可通過操作系統配置生成 core 文件 ulimit -c unlimited,將 core 文件提供給開發者以復現問題並進行排查。

數據字典的快速初始化

tuple 數據讀取基本原理

在數據庫無法啓動的情況下,PDU 仍能完成數據字典的初始化,這一過程基於對 tuple 數據讀取方式的分析與實現。

在 PostgreSQL 中,每個數據頁(Page)開頭的頁頭(Page Header)存儲了頁的基本信息。頁頭中各字段都有固定的字節長度與偏移量,例如 pd_lower 佔 2 個字節,表示空閒空間的起始位置,即最後一個行指針(Line Pointer)的結束地址。在數據文件中,每行顯示 8 個字節,通過偏移量即可確定各結構的具體位置。
圖片10.png

數據頁的核心構成

數據頁主要由兩部分組成:目錄(Line Pointer 數組) 和 數據區域(Tuple Data)。目錄用於記錄每條元組在頁內的偏移量、標誌位及長度信息,每個行指針佔 4 個字節,用於定位對應的數據記錄。
圖片11.png

獲取到單條 ltemld 之後,在偏移量 lp_off 獲取 tuple。
圖片12.png

從 tuple 中定位數據區域

當定位到具體元組後,可通過行指針獲取其頭部信息(Tuple Header)。其中,t_hoff 表示元組頭長度,用於計算實際數據的起始位置;lp_len 表示元組總長度。由於 PostgreSQL 的數據存儲中列與列之間不存在分隔符,因此在解析時必須嚴格按照數據類型定義逐字節讀取,確保解析出的長度與目錄記錄一致,才能確認數據讀取正確。
圖片13.png

5 張基表

聚焦於系統在解析存儲文件中 5 張基表 時的處理機制。通過讀取並解析這些基表中的 tuple,系統能夠還原數據庫對象的定義信息,包括表結構、字段屬性以及對象間的依賴關係。該過程為數據字典的初始化提供了底層支撐,是數據庫啓動階段恢復核心元數據的關鍵步驟。
圖片14.png

  1. 從 global/1262 中獲取數據庫的 oid 得到對應目錄。
  2. 從“數據庫 oid/2615”“數據庫 oid/1259”“數據庫 oid/1249”“數據庫 oid/1247”5 張基表中獲取整庫的所有記錄。
  3. 按照上圖的對應關係拼湊出每張表的所有信息。

PDU 數據字典最終形態

PDU 在獲取數據字典後,會將解析結果存儲在可執行目錄下的 meta 原數據目錄中。系統會為每個數據庫生成對應的 \_tables.txt 文件,例如檢測到數據庫 alldb,則會在 meta 下生成 clothes_space_tables.txt 文件。
文件中每一行代表一張表,包含表 ID、文件號(默認一致)、TOAST ID 及文件號、模式 ID、表名、列名、列類型名、列數量與類型長度等。最後兩列為列長度和類型對齊方式,其中列長度為 -1 表示變長類型(如 varchar、numeric),定長類型(如 int、float)則對應具體字節長度。對齊方式用於控制字節排列,以保障解析與計算的準確性。

下圖為數據字典最終形態:
圖片15.png

踩坑指南

在 PostgreSQL 數據文件和 TOAST 文件的脱離數據庫讀取過程中,有幾類情況需要格外注意,以避免解析錯誤或數據丟失。

  1. drop 列需在初始化與數據解析階段進行特殊處理。
    圖片16.png
  2. reltoastrelid 不是 filenode,只是 oid。
    圖片17.png
  3. 在脱離數據庫環境讀取 TOAST 數據時,需要格外謹慎。

默認情況下,toast oid 與 toast filenode 一致, external 結構體中保存的是 toast oid 。

vacuum full/truncate 之後,兩者不再一致,但是 external 結構體中存的依然是 toast oid。
圖片18.png

國產 PG 係數據庫適配

在國產 PostgreSQL 係數據庫的適配過程中,不同廠商對數據文件和 WAL 日誌均有一定修改。

在數據文件頁頭方面,一些數據庫增加了額外字節或填充零,使得原生讀取函數無法直接解析。通過分析固定的字節模式,可以推斷新增字段或填充值的位置,從而調整讀取邏輯,使頁頭的 lower、upper 等關鍵字段正確解析。

在 WAL 日誌適配方面,部分數據庫增加了額外類型,改變了原有類型的編號順序。為保證數據恢復功能,需要重新映射類型 ID,以正確識別 XLOG、HEAP、BTREE 等關鍵記錄。

PDU 核心價值與性能優化

PDU 分為社區版和專業版,專業版的許可與服務器綁定。PDU 的核心價值在於數據恢復的速度——對於急需恢復生產環境的客户,解析數據的效率至關重要。

在實際使用中,TOAST 數據的解析是性能瓶頸所在。社區版存在限速,專業版全速解析時也會因 TOAST 數據量大而明顯下降。為提升性能,開發者嘗試了多線程,但發現瓶頸依然在 TOAST 解析上。針對這一問題,進行了結構優化:原本的全表掃描方式改為類似目錄索引的解析方式。以一張 200MB、含 18,045 條數據的高密度 TOAST 表為例,優化前解析耗時約 800 秒,優化後降至 1.6 秒,同時解析結果與原數據完全一致。

這一優化顯著提升了 PDU 的實際應用效率,也體現了其在災難恢復場景下的核心價值。

結語

通過對 PostgreSQL 內核數據結構的深度解析和性能優化,PDU 顯著提升了大規模數據恢復的效率與準確性,為數據庫運維提供了可靠、可落地的災難恢復能力,同時兼顧不同廠商版本的兼容性,實現了高效、安全的數據恢復實踐。

user avatar matrixorigin 头像 zread_ai 头像 cppfudaodeagan 头像
点赞 3 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.