PostgreSQL数据库恢复工具FGPDU(FGEDU PostgreSQL DUL)

FGPDU(Full name: FGEDU PostgreSQL Data UnLoader) 是一款面向 PostgreSQL 数据库的离线数据抽取与灾难恢复工具,是 PostgreSQL 生态中少有的、可在数据库实例无法启动的情况下,直接从底层数据文件中恢复数据的"最后防线"型工具。

当前版本:v1.0.7

适用 PostgreSQL:9.2 ~ 19

适用平台:Linux(RHEL/OEL/CentOS/Ubuntu/SUSE/Kylin/UOS 等)+ Windows(XP~11,Server 2003~2022,x86 & x64)

一、程序介绍

1.1 工具概述

在企业级数据库运维场景中,数据丢失和数据库无法启动是最严重、最令人紧张的故障之一。常见诱因包括:

  • 硬件故障或文件系统损坏导致数据文件损坏;
  • 误操作(误执行 DELETE / DROP TABLE / TRUNCATE TABLE);
  • PostgreSQL 升级失败、系统表(pg_class / pg_database / pg_attribute)损坏;
  • 控制文件 pg_control 丢失或不可读,实例启动时直接抛出 PANIC
  • WAL 日志损坏、回放失败,数据库无法到达一致状态;
  • 误删除数据库目录或被加密;
  • 备份策略失效,没有可用的物理/逻辑备份。

在上述任何一种情况下,PostgreSQL 自身通常已经无法启动,传统的 pg_dump / pg_basebackup / PITR 等手段都依赖于数据库实例在线或归档日志完整。FGPDU 正是为这种"实例已经死掉、备份又不全"的极端场景而生:它完全绕过 PostgreSQL 服务进程 ,直接读取磁盘上的数据文件(base/<db_oid>/<relfilenode>),解析 PostgreSQL 的 8KB 数据页格式(PageHeaderDataItemIdDataHeapTupleHeaderData),将页面中的元组(tuple)抽取出来,重建为可读的 SQL(CREATE TABLE + INSERT INTO)、CSV、TXT、DMP、Binary 等多种格式,输出到指定目录后即可在新库中重新导入,完成数据"起死回生"。

1.2 工具定位与设计哲学

FGPDU 的核心设计哲学可以概括为以下五点:

  1. 离线优先,只读安全 :FGPDU 不需要 PostgreSQL 进程在线,对原始数据目录采取严格只读策略,不会对数据文件做任何写入或修改操作。即使恢复过程出错,原始数据状态保持不变,可以反复尝试不同的恢复策略。
  2. 最大恢复:默认采用"最大数据恢复"策略------遇到坏块自动跳过继续扫描后续块;遇到页头损坏自动回退到原始元组扫描;遇到系统表损坏自动降级为文件级紧急扫描。一切以"尽可能多恢复一行数据"为目标。
  3. 零运行时依赖:编译产物为单文件可执行程序(Linux 静态 ELF、Windows 静态 PE),不需要安装任何运行时库(不依赖 glibc 动态库、不需要 VC++ 运行时、不需要 MinGW DLL),拷贝到目标服务器即可运行。这对生产环境中的应急恢复至关重要------故障机器往往无法联网安装依赖。
  4. 跨平台广覆盖:覆盖 Linux 主流发行版(含 RHEL 5/6/7/8/9/10、麒麟、UOS 等国产系统)与 Windows XP~11 全系列桌面和服务器版本(含 x86 与 x64),尤其对老旧系统做了专门适配。
  5. 命令简洁直观 :所有操作通过简短的子命令完成(diagnose / list / scan / recover / scan-file),运维人员无需记忆复杂参数即可上手。

1.3 适用场景

FGPDU 适用于以下任一场景:

场景 描述
数据库无法启动 PostgreSQL 实例启动失败、控制文件损坏、WAL 损坏、系统表损坏等导致无法正常进入
DELETE 数据恢复 误执行 DELETE FROM table WHERE ... 后,VACUUM 之前抢救数据
DROP 表恢复 误执行 DROP TABLE table,pg_class 条目被删但数据文件仍残留在磁盘
TRUNCATE 表恢复 误执行 TRUNCATE TABLE,旧 relfilenode 文件成为孤儿文件
数据迁移取证 需要将数据库数据导出为 SQL/CSV 用于审计、迁移或异地恢复
跨版本数据迁移 老版本 PG 实例无法启动时,抽取数据导入新版本 PG
坏块数据抢救 数据文件部分块损坏,常规导入失败,需要尽可能恢复未损坏部分
国产化环境恢复 麒麟、UOS、NeoKylin 等国产 Linux 系统下的 PostgreSQL 数据恢复

1.4 工作原理

FGPDU 的工作原理可以分解为以下五个层次:

  1. 目录诊断层 :扫描 PGDATA 目录结构,读取 PG_VERSION 文件确认 PG 版本,读取 global/pg_control 获取块大小等控制信息,校验 base/global/pg_wal/pg_tblspc/ 等关键目录是否完整。
  2. 系统目录解析层 :解析 pg_databasepg_classpg_namespacepg_attributepg_tablespace 等系统表,建立"数据库 OID ↔ 数据库名"、"relfilenode OID ↔ 表名 + Schema + 列定义"的映射字典。
  3. 数据页解析层 :根据 PG 版本对应的页面格式(page version 1~4)解析每个 8KB 数据页:解析页头 PageHeaderDataItemId 数组、HeapTupleHeaderData,提取元组的 t_xmin / t_xmax / t_infomask / t_infomask2 等事务信息,并定位元组数据区。
  4. 元组数据提取层 :根据 pg_attribute 中记录的列定义(atttypidattlenattalignattstorageatttypmodattnotnull)按 PG 内部对齐规则解析每列的值,处理定长/变长类型、NULL 位图、TOAST 外置数据、压缩数据、枚举类型、复合类型、数组类型等。
  5. 输出层:将解析出的元组按指定格式(SQL/CSV/TXT/DMP/Binary)写入到输出目录,并在 SQL 文件末尾自动追加"恢复步骤说明 + 数据校验 SQL",方便用户直接复制执行完成后续恢复。

1.5 作者信息

联系方式 信息
作者 风哥
官方网站http://www.fgedu.net.cn , http://www.itpux.com
数据库教程https://edu.51cto.com/lecturer/8020378.html

二、功能特性

2.1 离线数据抽取能力

FGPDU 最核心的能力是完全脱离 PostgreSQL 服务进程完成数据抽取。具体表现为:

  • 不需要 postgresql.confpg_hba.conf 完整可用;
  • 不需要 pg_control 完整可用(缺失时自动使用默认 8192 字节块大小);
  • 不需要 WAL 日志完整可用;
  • 不需要 shared_bufferswork_mem 等运行时参数;
  • 只需要 base/<db_oid>/<relfilenode> 数据文件可读即可工作。

这意味着即使数据目录只剩下一个 base/ 子目录的拷贝,FGPDU 也能从中恢复数据。

2.2 强大的数据恢复能力

FGPDU 支持三种 PostgreSQL 数据库中最常见的"误删除"恢复:

  1. DELETE 数据恢复

    • 原理:DELETE FROM 操作不会立即物理删除行,而是将行的 t_xmax 标记为非零,并在 t_infomask 中设置 HEAP_DELETED 标志,等待 VACUUM 清理。
    • 实现:FGPDU 扫描每个数据块,检测 t_xmax != 0 的行,提取尚未被 VACUUM 物理清除的删除数据。
    • 适用条件:必须在 VACUUM 运行之前执行恢复。
  2. DROP 表数据恢复

    • 原理:DROP TABLE 会删除 pg_class 中的元数据条目,但数据文件(relfilenode)仍然保留在磁盘上,直到被文件系统回收或 autovacuum 清理。
    • 实现:FGPDU 扫描 base/<dboid>/ 目录下所有数据文件,与 pg_class 中已知 OID 对比,找出"孤立文件"(pg_class 中不存在但磁盘上仍存在),从这些文件中提取数据。
    • 适用条件:数据文件尚未被文件系统回收。
  3. TRUNCATE 表数据恢复

    • 原理:TRUNCATE TABLE 会创建新的 relfilenode 文件替换旧文件,旧文件成为孤儿文件。
    • 实现:与 DROP 恢复逻辑相同,扫描孤立 relfilenode 文件。
    • 适用条件:旧的 relfilenode 文件尚未被回收。

2.3 精确的多维度过滤

FGPDU 支持三个维度的组合过滤,实现"精确恢复",避免不必要的数据扫描和混乱的输出文件:

维度 选项 说明
用户名/数据库名 -u--user-D / --database 限定只导出某个数据库(PG 中用户名通常与数据库名一致)
Schema -s--schema 限定只导出某个 Schema 下的所有表
表对象 -t--table 限定只导出某张表,支持 schema.table 格式或纯表名(自动跨 Schema 匹配)

三个维度可以任意组合,例如:

bash 复制代码
# 用户 + Schema + 表 全组合精确导出
fgpdu scan -d /postgresql/data -u mydb -s public -t orders -o /tmp/export

2.4 多种输出格式

FGPDU 支持五种输出格式,覆盖不同的恢复与迁移需求:

格式 选项值 文件后缀 用途 可读性 导入方式
SQL sql(默认) .sql 包含 CREATE TABLE + INSERT INTO 语句,可直接用 psql -f 导入新库 psql -d newdb -f table.sql
CSV csv .csv 第一行为列名,适合 Excel/BI 工具导入或异构数据库迁移 \copyCOPY ... FROM
TXT txt .txt 文本格式,字段用分隔符分隔,便于脚本处理 自定义脚本
DMP dmp .dmp FGPDU 专用二进制格式,包含表结构元数据和原始数据,用于大规模快速恢复 FGPDU 工具自身
Binary binary .bin 原始二进制数据导出,不做转换,适合取证分析 二进制工具

2.5 损坏弹性扫描(自适应扫描)

这是 FGPDU 区别于简单 pg_dump 类工具的关键能力之一。当数据文件部分损坏时,FGPDU 仍能最大程度恢复数据:

  1. 正常扫描模式:通过页头和 item pointer 读取元组(最快、最准确,适用于健康数据页)。
  2. 原始扫描模式(Raw Scan):当页头损坏或 item pointer 失效时,不依赖页头结构,直接在整个 8KB 页面中按元组模式搜索,尽可能捞出残留数据。
  3. 自由空间扫描(Free Space Scan) :扫描页面空闲空间(pd_lowerpd_upper 之间)中残留的已删除/覆盖元组数据,能恢复一些"看起来已经被覆盖"的数据。

启用 --continue-on-error 选项(默认启用)即可激活自适应扫描。遇到坏块时自动跳过继续扫描后续块,不会因为单个坏块中断整个恢复流程。

2.6 紧急全库扫描(EMERGENCY 模式)

pg_class / pg_database 等系统目录损坏或数据库完全无法启动时,FGPDU 会自动降级为文件级紧急扫描

  • 直接遍历 base/ 目录下所有数据库子目录;
  • 对每个数据库目录下的所有数据文件执行扫描;
  • 自动识别真正的 PG 数据文件,跳过 _fsm(free space map)、_vm(visibility map)、_initPG_VERSION 等辅助文件;
  • 恢复的表数据按数据库名(或 db_<OID>)分子目录导出。

无需人工干预,是数据库完全崩溃时的最后一道防线。

2.7 多段文件支持

PostgreSQL 大表(超过 1GB 默认 RELSEG_SIZE)会被自动分割为多个段文件:

  • 第一段:<oid>(如 16385
  • 第二段:<oid>.1(如 16385.1
  • 第三段:<oid>.2(如 16385.2

FGPDU 自动计算段号和偏移量,无需手动指定,正确处理大表的所有段文件,确保大表数据完整恢复。

2.8 数据完整性校验

FGPDU 对每个提取的元组进行多重完整性校验,过滤无效元组,提高恢复数据质量:

  • 字段数匹配 :元组实际列数与 pg_attribute 中记录的列数一致;
  • 数据类型最小长度检查:每个字段的实际长度符合该类型的最小要求;
  • NULL bitmap 一致性:NULL 标记位与字段实际内容一致;
  • xmin/xmax 合理性t_xmin 在合理范围(1~10 亿),t_xmaxt_infomask 组合合理;
  • 元组数据区非全零、非重复模式:排除全零元组、重复模式元组;
  • 页内边界检查:防止 item pointer 越界导致读取越界内存。

统计 valid / partial / invalid 三类元组数量,便于评估恢复数据质量。

2.9 恢复步骤说明与数据校验 SQL

每个 SQL 输出文件末尾自动追加完整的恢复指南和数据校验 SQL:

  • 建库 → 建 Schema → 导入 → 验证 → 重建索引 → ANALYZE 的完整步骤说明;
  • 行数验证 SQL(SELECT COUNT(*) FROM schema.table);
  • NOT NULL 约束检查 SQL;
  • 数值列范围检查 SQL(MIN/MAX/DISTINCT);
  • 日期列范围检查 SQL;
  • 重复行检测 SQL(GROUP BY ... HAVING COUNT(*) > 1)。

用户无需自行编写验证 SQL,直接复制执行即可。

2.10 稳定性与安全加固

FGPDU 针对生产环境做了大量稳定性加固:

  • 信号处理(Linux) :捕获 SIGSEGV / SIGBUS / SIGABRT / SIGFPE / SIGILL,遇到损坏文件不崩溃,输出提示信息后安全退出;
  • 异常处理(Windows) :使用 SetUnhandledExceptionFilter + MiniDumpWriteDump 做崩溃保护(SEH);
  • 内存安全:所有扫描函数中的栈上大数组(page_buf8KB、tuples12KB、items2KB)改为动态内存分配,彻底消除栈溢出风险;
  • 缓冲区溢出保护 :所有 memcpy / strncpy 操作都做了严格的边界检查;
  • 路径安全 :路径长度检查(FGPDU_MAX_PATH = 4096),防止超长路径导致的缓冲区溢出;
  • 忽略 SIGPIPE:输出重定向到管道时不会因为下游进程退出导致 FGPDU 崩溃;
  • 磁盘满检测 :输出写入时检测 ferror,遇到磁盘满等错误立即报错。

2.11 跨平台与跨版本能力

  • 跨 PostgreSQL 版本 :覆盖 PG 9.2 ~ PG 19,针对每个版本对应的页面格式(page version 1~4)做了精确适配,包括 PG 13+ 引入的 pd_prune_xid 字段;
  • 跨 Linux 发行版:RHEL/OEL/CentOS 5/6/7/8/9/10、Ubuntu 12.04+、Debian 7+、SUSE 11+、Kylin、UOS、NeoKylin 等国产系统;
  • 跨 Windows 版本:Windows XP SP3 ~ Windows 11,Server 2003 ~ Server 2022,x86 与 x64 全支持;
  • 跨架构:x86_64 与 i686(32 位)双架构支持。

2.12 进度与详细日志

  • -v / --verbose:详细模式,输出每个表的处理情况(表名、OID、页数、恢复元组数、坏块数等);
  • --progress:进度模式,适合大数据量场景实时查看扫描进度;
  • 日志可重定向到文件:fgpdu scan ... -v 2>&1 | tee /tmp/fgpdu.log

三、支持环境

3.1 操作系统支持

3.1.1 Linux x86_64 / i686
发行版家族 具体版本
Red Hat 系 RHEL / OEL / CentOS 5 / 6 / 7 / 8 / 9 / 10、CentOS Stream 8 / 9
Ubuntu Ubuntu 12.04 LTS ~ 24.04 LTS
Debian Debian 7+
SUSE SUSE Linux Enterprise Server (SLES) 11 / 12 / 15、openSUSE 12+
国产系统 银河麒麟(Kylin)、统信 UOS、中标麒麟(NeoKylin)
通用 任何基于 kernel 2.6.18+(RHEL5)或 2.6.32+(RHEL6+)的 x86 / x86_64 Linux

重要提示 :在 RHEL 5 / 6 / 7 等老系统上部署时,建议使用对应的 make rhel5 / make rhel6 / make rhel7 编译目标,避免 glibc 版本不匹配导致的 GLIBC_2.x not found 错误。

3.1.2 Microsoft Windows(x86 & x64)
版本家族 具体版本
客户端 Windows XP SP3、Vista、7、8、8.1、10、11
服务器端 Windows Server 2003、2008、2008 R2、2012、2012 R2、2016、2019、2022
架构 x86(32 位)与 x64(64 位)均原生支持

Windows 版本通过 MinGW-w64 交叉编译或 Visual Studio 原生编译生成,输出为全静态单文件 fgpdu.exe,无需任何运行时 DLL 依赖。

3.2 PostgreSQL 版本支持

主版本 具体版本 页面版本
PG 9.x 9.2、9.3、9.4、9.5、9.6 page version 1 / 2 / 3
PG 10.x 10 page version 4
PG 11.x 11 page version 4
PG 12.x 12 page version 4
PG 13.x 13(含 pd_prune_xid 字段) page version 4
PG 14.x 14 page version 4
PG 15.x 15 page version 4
PG 16.x 16 page version 4
PG 17.x 17 page version 4
PG 18.x 18 page version 4
PG 19.x 19 page version 4

3.3 硬件要求

项目 最低要求 推荐
CPU x86 / x86_64,1 核 x86_64,多核
架构 x86(32 位)或 x64(64 位) x64
内存 256 MB 1 GB+(大数据量建议 8 GB+)
磁盘剩余空间 数据目录大小的 1.5 倍 数据目录大小的 2 倍+(输出目录使用 SSD 更佳)
网络 无需联网 无需联网(完全离线运行)

3.4 编译依赖

3.4.1 Linux 编译依赖
bash 复制代码
# RHEL / OEL / CentOS(含 RHEL5/6/7)
yum install gcc make glibc-static

# Ubuntu / Debian
apt-get install gcc make libc6-dev

# SUSE / SLES
zypper install gcc make glibc-devel-static

# 麒麟 / UOS(基于 yum)
yum install gcc make glibc-static
  • gcc 版本:最低 gcc 4.1(RHEL5 默认),推荐 gcc 4.8+
  • make:GNU make 3.81+
  • glibc-static :仅完全静态编译(make standalone)时必需,半静态编译(make portable)无需此包
3.4.2 Windows 交叉编译依赖(在 Linux 上编译出 fgpdu.exe)
bash 复制代码
# RHEL / Fedora / CentOS(EPEL 源)
yum install mingw64-gcc mingw32-gcc mingw64-binutils mingw32-binutils

# Ubuntu / Debian
apt-get install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 binutils-mingw-w64
3.4.3 Windows 原生编译依赖(MSVC)
  • Visual Studio 2015 / 2017 / 2019 / 2022 任一版本
  • 或 Windows SDK 8.1+ 独立安装包
  • 通过 "Developer Command Prompt for VS" 调用 cl.exe

3.5 权限要求

  • 对 PostgreSQL 数据目录有读权限 (通常属主是 postgres 用户);
  • 对输出目录有读写权限
  • 不需要 root 权限(但生产环境通常用 root 拷贝/部署,需注意切换用户运行);
  • 不需要 PostgreSQL 超级用户密码(FGPDU 直接读文件,不连数据库)。

3.6 网络与外部依赖

  • 无需联网:FGPDU 是完全离线工具,运行时不访问任何网络资源;
  • 无需 PG 服务在线:FGPDU 直接读文件,PostgreSQL 服务可以处于停止状态;
  • 无需 Java / Python / .NET 运行时:静态编译的单文件,零运行时依赖。

四、程序使用

4.1 编译与安装

4.1.1 Linux 编译
bash 复制代码
cd /path/to/FGPDU

# ============================================================
# 方式 A:现代 Linux 编译(RHEL7+/Ubuntu16+/SUSE12+,推荐)
# ============================================================
make standalone       # 全静态单文件(需要 glibc-static)
# 或
make portable         # 半静态(仅依赖 libc),无需 glibc-static

# ============================================================
# 方式 B:为 RHEL 5/6/7 老系统专门编译(生产部署时使用)
# ============================================================
make rhel5            # 兼容 RHEL 5.11 / CentOS 5.x / glibc 2.5
make rhel6            # 兼容 RHEL 6.x / CentOS 6.x / glibc 2.12
make rhel7            # 兼容 RHEL 7.x / CentOS 7.x / glibc 2.17

# ============================================================
# 方式 C:调试版本(带 gdb 符号,未优化,便于排错)
# ============================================================
make debug

# ============================================================
# 方式 D:发布版本(带优化)
# ============================================================
make release

编译产物位置:

编译目标 产物路径
make standalone build/standalone/fgpdu
make portable build/portable/fgpdu
make rhel5 build/rhel5/fgpdu
make rhel6 build/rhel6/fgpdu
make rhel7 build/rhel7/fgpdu
make debug build/debug/fgpdu
make release build/release/fgpdu
4.1.2 Windows 编译

方式 A:在 Linux 上交叉编译(推荐)

bash 复制代码
# 生成 64 位 Windows EXE(PE32+,推荐)
make windows-mingw      # 输出:build/win64/fgpdu.exe

# 生成 32 位 Windows EXE(PE32,兼容 XP x86)
make windows-mingw32    # 输出:build/win32/fgpdu_x86.exe

生成的 .exe 为全静态单文件,不需要 VC++ 运行时、不需要 MinGW 运行时、不依赖任何 .dll,直接拷贝到目标 Windows 机器即可运行。

方式 B:在 Windows 上用 MSVC 原生编译

打开 "Developer Command Prompt for VS 2015/2017/2019/2022":

bat 复制代码
cd \path\to\FGPDU
build\windows-msvc.bat
REM 产物:build\msvc\fgpdu.exe
4.1.3 编译验证
bash 复制代码
# Linux
./build/standalone/fgpdu -V
./build/standalone/fgpdu -h

# Windows
fgpdu.exe -V
fgpdu.exe -h

# 检查 Linux 二进制依赖(应仅依赖 libc 或零依赖)
make check-deps

预期 -V 输出:

复制代码
FGPDU - FGEDU PostgreSQL DUL v1.0.0
Supports PostgreSQL: 9.2 - 19
Supports RHEL: 5 - 10
Supports: OEL, Ubuntu, SUSE, CentOS, Kylin, UOS
Supports: Windows XP/Vista/7/8/10/11 x86/x64
License: Educational/Research Use Only
4.1.4 安装到系统路径(可选)
bash 复制代码
make install           # 安装到 /usr/local/bin/fgpdu
# 或手动拷贝
cp build/standalone/fgpdu /usr/local/bin/
chmod +x /usr/local/bin/fgpdu

4.2 命令详解

4.2.1 命令总览
命令 说明
fgpdu diagnose -d PGDATA 诊断数据目录结构完整性
fgpdu list tables -d PGDATA 列出所有用户表
fgpdu list databases -d PGDATA 列出所有数据库
fgpdu list schemas -d PGDATA 列出所有 Schema
fgpdu scan -d PGDATA -o OUT 抽取所有表数据(默认 SQL 格式)
fgpdu recover delete -d PGDATA -o OUT 恢复 DELETE 删除行
fgpdu recover drop -d PGDATA -o OUT 恢复 DROP 表数据
fgpdu recover truncate -d PGDATA -o OUT 恢复 TRUNCATE 表数据
fgpdu scan-file FILE -o OUT 扫描单个数据文件
4.2.2 命令行选项
选项 短选项 说明 示例
--data-dir -d PostgreSQL 数据目录路径(必需) -d /postgresql/data
--output-dir -o 输出目录 -o /tmp/recover
--format -f 输出格式:sql/csv/txt/dmp/binary -f csv
--user -u 用户名/数据库名 -u postgres
--database -D 数据库名(-u 别名) -D mydb
--schema -s Schema 名称 -s public
--table -t 表名(支持 schema.table -t public.orders
--include-deleted --- 包含已删除行 --include-deleted
--include-updated --- 包含已更新行 --include-updated
--verbose -v 详细输出 -v
--progress --- 显示进度 --progress
--continue-on-error --- 遇错继续(默认开启自适应扫描) --continue-on-error
--version -V 显示版本信息 -V
--help -h 显示帮助 -h
4.2.3 诊断命令(diagnose)

诊断命令是恢复操作的第一步,用于检查数据目录结构完整性。

bash 复制代码
# 基本诊断
fgpdu diagnose -d /postgresql/data

# 详细诊断
fgpdu diagnose -d /postgresql/data -v

诊断输出示例:

复制代码
=== FGPDU Diagnostics ===

Data Directory: /postgresql/data
PostgreSQL Version: 15.2
pg_control: OK (8192 bytes)
Block size: 8192 bytes
base/ directory: OK
  Databases found: 2
    - 1
    - 16384
global/ directory: OK
pg_wal/ directory: OK

Permission Check:
  Read access: OK

System Catalog Check:
  IO Engine: OK
  pg_class: OK
    Page version: 4
    Items on page: 128

=== Diagnostics Complete ===

诊断结果解读:

检查项 含义 处理建议
PostgreSQL Version 从 PG_VERSION 文件读取 确认版本在 9.2~19 范围内
pg_control 控制文件状态 缺失时块大小使用默认 8192
Block size 数据块大小 自动从 pg_control 识别
base/ directory 用户数据库目录 必须存在
global/ directory 系统表目录 必须存在
pg_wal/ directory WAL 日志目录 检查是否存在
Read access 读权限检查 需要 postgres 用户权限
pg_class 系统表完整性 FAILED 时使用 scan-file 直接扫描
4.2.4 列表命令(list)
bash 复制代码
# 列出所有数据库
fgpdu list databases -d /postgresql/data

# 列出所有表
fgpdu list tables -d /postgresql/data

# 列出指定 schema 的表
fgpdu list tables -d /postgresql/data -s public

# 查找指定表名(跨 schema)
fgpdu list tables -d /postgresql/data -t orders

# 列出指定 schema.table
fgpdu list tables -d /postgresql/data -t public.orders

# 列出所有 schema
fgpdu list schemas -d /postgresql/data
4.2.5 数据抽取命令(scan)

scan 命令是最常用的全库导出命令。

bash 复制代码
# 全库导出为 SQL(默认)
fgpdu scan -d /postgresql/data -o /tmp/export

# 指定格式
fgpdu scan -d /postgresql/data -o /tmp/export -f csv
fgpdu scan -d /postgresql/data -o /tmp/export -f dmp

# 指定表
fgpdu scan -d /postgresql/data -t public.orders -o /tmp/export

# 按 Schema
fgpdu scan -d /postgresql/data -s public -o /tmp/export

# 按用户名/数据库名
fgpdu scan -d /postgresql/data -u postgres -o /tmp/export

# 三维度组合
fgpdu scan -d /postgresql/data -u mydb -s public -t orders -o /tmp/export

# 最大恢复模式(推荐)
fgpdu scan -d /postgresql/data -o /tmp/export -v --progress --continue-on-error

输出目录结构示例(按数据库分目录):

复制代码
/tmp/fgpdu_export/
├── postgres/              ← postgres 数据库
│   ├── public_orders.sql
│   └── public_customers.sql
├── fgedudb/               ← fgedudb 数据库
│   ├── fgeduschema_fgedu01.sql
│   └── fgeduschema_fgedu02.sql
└── db_16384/              ← 无法解析名称时用 OID
    └── recovered_16521.sql
4.2.6 数据恢复命令(recover)
bash 复制代码
# 恢复 DELETE 数据
fgpdu recover delete -d /postgresql/data -o /tmp/recover

# 恢复 DROP 表数据
fgpdu recover drop -d /postgresql/data -o /tmp/recover

# 恢复 TRUNCATE 表数据
fgpdu recover truncate -d /postgresql/data -o /tmp/recover

# 恢复指定表的 DELETE 数据
fgpdu recover delete -d /postgresql/data -t public.orders -o /tmp/recover

# 恢复指定用户+Schema+表
fgpdu recover delete -d /postgresql/data -u mydb -s public -t orders -o /tmp/recover

# 详细模式
fgpdu recover delete -d /postgresql/data -o /tmp/recover -v

恢复输出示例:

复制代码
=== DELETE Recovery Mode ===

Data directory: /postgresql/data
Output directory: /tmp/recover

Tables found: 42

  Processing: public.orders
    Blocks scanned: 128
    Deleted tuples found: 56
    Data still intact: 48
    Successfully recovered: 42

=== DELETE Recovery Complete ===
Tables scanned: 42
Deleted rows found: 156
Rows recovered: 128
Time elapsed: 2.53 seconds
================================
4.2.7 单文件扫描命令(scan-file)

当系统表损坏、无法通过正常方式列出表时,可以直接扫描指定的 relfilenode 文件。

bash 复制代码
# 基本用法
fgpdu scan-file /postgresql/data/base/16384/1259 -o /tmp/export

# 指定输出格式
fgpdu scan-file /postgresql/data/base/16384/1259 -o /tmp/export -f csv
fgpdu scan-file /postgresql/data/base/16384/1259 -o /tmp/export -f dmp

# 详细模式
fgpdu scan-file /postgresql/data/base/16384/1259 -o /tmp/export -v

文件路径格式:PGDATA/base/<db_oid>/<relfilenode>

输出文件命名规则:

  • SQL:output/<relfilenode>_recovered.sql
  • CSV:output/<relfilenode>_recovered.csv
  • DMP:output/<relfilenode>_recovered.dmp

4.3 输出格式详解

4.3.1 SQL 格式(默认)

生成标准的 SQL 文件,包含 CREATE TABLEINSERT INTO 语句:

sql 复制代码
-- FGPDU Recovery Output
-- Table: public.orders
-- OID: 16385
-- Pages: 128
-- Generated: 2026-08-07 10:00:00

-- DROP TABLE IF EXISTS public.orders;

CREATE TABLE public.orders (
    id integer,
    order_date timestamp,
    customer_id integer,
    amount numeric(10,2),
    status varchar(20)
);

INSERT INTO public.orders VALUES (1, '2024-01-15 10:30:00', 1001, 1500.00, 'completed');
INSERT INTO public.orders VALUES (2, '2024-01-15 11:00:00', 1002, 2300.50, 'pending');

-- ... 文件末尾自动追加恢复步骤说明与验证 SQL ...

导入方式:

bash 复制代码
psql -d newdb -f /tmp/export/public_orders.sql
4.3.2 CSV 格式
csv 复制代码
id,order_date,customer_id,amount,status
1,2024-01-15 10:30:00,1001,1500.00,completed
2,2024-01-15 11:00:00,1002,2300.50,pending

导入方式:

sql 复制代码
\copy public.orders FROM '/tmp/export/public_orders.csv' WITH CSV HEADER;
4.3.3 TXT 格式

文本格式,每行一个元组,字段用分隔符分隔,便于自定义脚本处理。

4.3.4 DMP 格式

FGPDU 专用的二进制格式,包含表结构元数据和原始数据,适合大规模快速恢复。

4.3.5 Binary 格式

原始二进制数据导出,不做任何转换,适合取证分析。

4.4 高级用法

4.4.1 配置文件模式

创建配置文件 fgpdu.conf

ini 复制代码
# FGPDU 配置文件
data_dir=/postgresql/data
output_dir=/tmp/recover
format=sql
verbose=true
continue_on_error=true

使用配置文件:

bash 复制代码
fgpdu -c fgpdu.conf
4.4.2 详细日志与进度
bash 复制代码
# 详细模式
fgpdu scan -d /postgresql/data -o /tmp/recover -v

# 进度模式
fgpdu scan -d /postgresql/data -o /tmp/recover --progress

# 日志重定向到文件
fgpdu scan -d /postgresql/data -o /tmp/recover -v 2>&1 | tee /tmp/fgpdu.log
4.4.3 大型数据目录恢复
bash 复制代码
# 组合使用:最大恢复模式
fgpdu scan -d /postgresql/data -o /tmp/recover -v --progress --continue-on-error
4.4.4 并行恢复(脚本示例)
bash 复制代码
#!/bin/bash
PGDATA=/postgresql/data
OUTPUT=/tmp/parallel_recover
mkdir -p $OUTPUT

# 获取表列表
TABLES=$(fgpdu list tables -d $PGDATA -s public | grep "^public" | awk '{print $1"."$2}')

# 并行恢复(每次最多 4 个)
echo "$TABLES" | xargs -P 4 -I {} bash -c '
    table="{}"
    fgpdu scan -d '"$PGDATA"' -t "$table" -o '"$OUTPUT"' -f sql
    echo "Done: $table"
'
echo "All tables recovered."

五、程序各种案例场景与操作过程

5.1 案例 1:数据库无法启动 --- 全量数据导出

场景描述:PostgreSQL 因数据文件损坏、系统表损坏等原因无法启动,需要将全库数据导出到新库。

关键特性

  1. 如果 pg_class / pg_database 系统目录损坏导致无法列出表,FGPDU 会自动切换到 EMERGENCY 紧急文件级扫描 ,直接遍历 base/ 下所有数据文件恢复数据,无需系统目录。
  2. 按数据库分目录导出:FGPDU 自动在输出目录下按数据库名创建子目录,每个数据库的所有表分别导出到各自的子目录中。
  3. 按用户名 unload :使用 -u <用户名/数据库名> 参数可以只导出指定数据库的所有表。

操作步骤

bash 复制代码
# ============================================================
# 步骤 1:停止 PostgreSQL 服务
# ============================================================
systemctl stop postgresql

# ============================================================
# 步骤 2:备份原始数据目录(非常重要!避免在原目录上操作)
# ============================================================
cp -a /postgresql/data /backup/pgdata_backup_$(date +%Y%m%d)

# ============================================================
# 步骤 3:诊断数据目录
# ============================================================
fgpdu diagnose -d /postgresql/data -v

# ============================================================
# 步骤 4:列出数据库与表(如果系统表损坏,可能返回 0 表,FGPDU 会自动降级)
# ============================================================
fgpdu list databases -d /postgresql/data
fgpdu list tables -d /postgresql/data > /tmp/tables_list.txt
cat /tmp/tables_list.txt

# ============================================================
# 步骤 5:全量导出所有数据为 SQL 格式(自动按数据库分目录)
# 注意:如果 pg_class/pg_database 损坏,FGPDU 会自动切换到
#       EMERGENCY 紧急文件级扫描模式
# ============================================================
mkdir -p /tmp/fgpdu_export
fgpdu scan -d /postgresql/data -o /tmp/fgpdu_export -v --continue-on-error

# ============================================================
# 步骤 5b:按用户名/数据库名精确导出(只导出指定数据库的所有表)
# ============================================================
fgpdu scan -d /postgresql/data -u fgedudb -o /tmp/fgpdu_export_fgedudb -v --continue-on-error

# 也可以导出为 DMP 格式(一表一文件)
fgpdu scan -d /postgresql/data -o /tmp/fgpdu_export -f dmp

# ============================================================
# 步骤 6:检查导出结果(按数据库子目录查看)
# ============================================================
ls -la /tmp/fgpdu_export/
for db_dir in /tmp/fgpdu_export/*/; do
    db_name=$(basename "$db_dir")
    file_count=$(ls "$db_dir"*.sql 2>/dev/null | wc -l)
    dir_size=$(du -sh "$db_dir" | cut -f1)
    echo "  $db_name: $file_count 个 SQL 文件, 大小 $dir_size"
done

# ============================================================
# 步骤 7:查看 SQL 文件中的恢复步骤说明
# ============================================================
tail -80 /tmp/fgpdu_export/fgedudb/*.sql | head -100

# ============================================================
# 步骤 8:在新服务器上创建新库并导入(遍历所有子目录)
# ============================================================
createdb newdb
for db_dir in /tmp/fgpdu_export/*/; do
    db_name=$(basename "$db_dir")
    echo "=== Importing database: $db_name ==="
    for f in "$db_dir"*.sql; do
        [ -f "$f" ] || continue
        echo "  Importing $(basename "$f")..."
        psql -d newdb -f "$f" || echo "WARNING: Failed to import $f"
    done
done

# ============================================================
# 步骤 9:验证数据
# ============================================================
psql -d newdb -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';"

# ============================================================
# 步骤 10(备选):当所有高层命令都失败时,直接扫描单个数据文件
# ============================================================
fgpdu scan-file /postgresql/data/base/16384/16521 -o /tmp/fgpdu_export -v

SQL 输出文件中的恢复步骤说明示例

每个 SQL 文件末尾会自动包含以下内容:

sql 复制代码
-- ============================================================
-- FGPDU Data Recovery Report & Restore Guide
-- ============================================================
-- Recovery Time:     2026-08-07 12:00:00
-- Tool:              FGPDU - FGEDU PostgreSQL DUL v1.0.0
-- Table:             public.orders (OID: 16385)
-- Rows Recovered:    1256
-- Bad Blocks:        3 (skipped, data partially recovered)
-- ============================================================
--
-- RESTORE STEPS:
--   Step 1: CREATE DATABASE recovered_db;
--   Step 2: CREATE SCHEMA IF NOT EXISTS public;
--   Step 3: psql -d recovered_db -f public_orders.sql
--   Step 4: SELECT COUNT(*) FROM public.orders;
--   Step 5: REINDEX TABLE public.orders;
--   Step 6: ANALYZE public.orders;
--
-- ============================================================
-- Data Verification Queries
-- ============================================================
SELECT COUNT(*) AS total_rows FROM public.orders;
SELECT COUNT(*) AS null_violations FROM public.orders WHERE id IS NULL;
SELECT MIN(id), MAX(id), COUNT(DISTINCT id) FROM public.orders;
SELECT id, COUNT(*) FROM public.orders GROUP BY id HAVING COUNT(*) > 1;

5.2 案例 2:误执行 DELETE --- 恢复删除数据

场景描述 :用户误执行 DELETE FROM orders WHERE status='cancelled',删除了大量订单数据,需要恢复。

前提:尚未执行 VACUUM!如果已执行 VACUUM,数据可能已被物理清除。

操作步骤

bash 复制代码
# 步骤 1:立即停止数据库(防止 autovacuum 清除数据)
systemctl stop postgresql
# 或设置 autovacuum=off 后重启
# pg_ctl -D /postgresql/data -o "-c autovacuum=off" start

# 步骤 2:备份数据目录
cp -a /postgresql/data /backup/pgdata_before_recover

# 步骤 3:恢复指定表的删除数据
mkdir -p /tmp/delete_recover
fgpdu recover delete -d /postgresql/data -t public.orders -o /tmp/delete_recover -v

# 步骤 4:检查恢复结果
ls -la /tmp/delete_recover/
cat /tmp/delete_recover/*.sql | head -50

# 步骤 5:恢复所有表的删除数据(如果多张表受影响)
fgpdu recover delete -d /postgresql/data -o /tmp/delete_recover_all -v

# 步骤 6:在新库或原库中导入恢复的数据
# 注意:先检查恢复的数据,确认无误后再导入
psql -d mydb -f /tmp/delete_recover/public_orders_deleted.sql

5.3 案例 3:误执行 DROP TABLE --- 恢复被删除的表

场景描述 :用户误执行 DROP TABLE important_table,需要恢复表数据。

前提:数据文件尚未被文件系统回收。

操作步骤

bash 复制代码
# 步骤 1:停止数据库
systemctl stop postgresql

# 步骤 2:备份数据目录
cp -a /postgresql/data /backup/pgdata_before_drop_recover

# 步骤 3:恢复被 DROP 的表数据
mkdir -p /tmp/drop_recover
fgpdu recover drop -d /postgresql/data -o /tmp/drop_recover -v

# 步骤 4:检查恢复结果
ls -la /tmp/drop_recover/
# 恢复的文件名格式: orphan_<oid>_recovered.sql

# 步骤 5:查看恢复的数据
cat /tmp/drop_recover/orphan_*_recovered.sql | head -100

# 步骤 6:如果知道被删除表的 OID,可以直接扫描该文件
ls -la /postgresql/data/base/16384/
fgpdu scan-file /postgresql/data/base/16384/16400 -o /tmp/drop_recover -f sql -v

# 步骤 7:重建表结构并导入恢复的数据
psql -d mydb -c "CREATE TABLE important_table (id int, name text, ...);"
psql -d mydb -f /tmp/drop_recover/orphan_16400_recovered.sql

5.4 案例 4:误执行 TRUNCATE --- 恢复被截断的表

场景描述 :用户误执行 TRUNCATE TABLE large_table,清空了表数据,需要恢复。

前提:旧的 relfilenode 文件尚未被回收(TRUNCATE 会创建新 relfilenode,旧文件成为孤儿文件)。

操作步骤

bash 复制代码
# 步骤 1:停止数据库
systemctl stop postgresql

# 步骤 2:备份数据目录
cp -a /postgresql/data /backup/pgdata_before_truncate_recover

# 步骤 3:恢复被 TRUNCATE 的表数据
mkdir -p /tmp/truncate_recover
fgpdu recover truncate -d /postgresql/data -o /tmp/truncate_recover -v

# 步骤 4:检查恢复结果
ls -la /tmp/truncate_recover/
cat /tmp/truncate_recover/*.sql | wc -l  # 查看恢复了多少行

# 步骤 5:导入恢复的数据
psql -d mydb -f /tmp/truncate_recover/orphan_*_recovered.sql

TRUNCATE 与 DROP 的区别

操作 pg_class 数据文件 可恢复性
DROP TABLE 条目被删除 旧文件保留 可恢复(直到文件被回收)
TRUNCATE TABLE 条目保留(新 relfilenode) 旧文件保留 可恢复(直到文件被回收)
DELETE 条目保留 文件不变 可恢复(直到 VACUUM)

5.5 案例 5:系统表损坏 --- 直接扫描数据文件

场景描述:pg_class 等系统表损坏,无法通过正常方式列出表,需要直接扫描数据文件。

关键特性 :当 fgpdu scan 发现系统目录无表时,会自动切换到 EMERGENCY 紧急文件级扫描 ,直接遍历 base/ 下所有数据文件,无需手动逐个扫描。

操作步骤

bash 复制代码
# 步骤 1:诊断确认系统表损坏
fgpdu diagnose -d /postgresql/data -v
# 如果显示 "pg_class: FAILED",说明系统表损坏

# 步骤 2(推荐):直接执行 scan,FGPDU 会自动降级为紧急扫描
mkdir -p /tmp/raw_recover
fgpdu scan -d /postgresql/data -o /tmp/raw_recover -v --continue-on-error
# 日志中会显示:
#   EMERGENCY FULL-DATABASE SCAN
#   Catalog is missing or corrupted.
#   Scanning every data file under base/ directly...

# 步骤 3(备选):直接浏览 base 目录,手动扫描单个文件
ls -la /postgresql/data/base/16384/
# 文件名就是 relfilenode(OID)

# 扫描单个文件
fgpdu scan-file /postgresql/data/base/16384/16385 -o /tmp/raw_recover -f sql -v

# 批量扫描所有文件(当自动扫描不生效时)
for f in /postgresql/data/base/16384/*; do
    if [ -f "$f" ]; then
        echo "=== Scanning: $f ==="
        fgpdu scan-file "$f" -o /tmp/raw_recover -f sql 2>&1 | grep "rows extracted"
    fi
done

# 步骤 4:检查恢复结果
ls -la /tmp/raw_recover/
echo "恢复文件数: $(ls /tmp/raw_recover/*.sql 2>/dev/null | wc -l)"

# 步骤 5:查看恢复的数据,手动识别表结构
for f in /tmp/raw_recover/*.sql; do
    echo "=== $(basename $f) ==="
    head -20 "$f"
    echo ""
done

5.6 案例 6:指定用户 + Schema + 表精确恢复

场景描述:只需要恢复特定用户(数据库)下、特定 Schema 中、特定表的数据。

操作步骤

bash 复制代码
# 步骤 1:先列出数据库,确认目标数据库
fgpdu list databases -d /postgresql/data
# 输出:
# OID    Name     Path
# 5      postgres 5
# 16384  mydb     16384

# 步骤 2:列出指定数据库的表
fgpdu list tables -d /postgresql/data -u mydb -s public

# 步骤 3:精确恢复 --- 指定用户+Schema+表
mkdir -p /tmp/precise_recover
fgpdu scan -d /postgresql/data \
    -u mydb \
    -s public \
    -t orders \
    -o /tmp/precise_recover \
    -f sql -v

# 步骤 4:恢复指定用户+Schema 下的删除数据
fgpdu recover delete -d /postgresql/data \
    -u mydb \
    -s public \
    -o /tmp/precise_recover

# 步骤 5:恢复指定用户的 DROP 表数据
fgpdu recover drop -d /postgresql/data \
    -u mydb \
    -o /tmp/precise_recover

# 步骤 6:批量恢复多张表
for table in orders users products employees; do
    echo "=== Recovering: public.$table ==="
    fgpdu scan -d /postgresql/data \
        -u mydb -s public -t $table \
        -o /tmp/precise_recover -f sql
done

5.7 案例 7:大表多段文件恢复

场景描述:一张 5GB 的大表被 TRUNCATE,数据分布在 5 个段文件中(每个约 1GB),需要恢复全部数据。

段文件命名规则

  • 第一段:<oid>(如 16385
  • 第二段:<oid>.1(如 16385.1
  • 第三段:<oid>.2(如 16385.2

操作步骤

bash 复制代码
# 步骤 1:确认段文件存在
ls -la /postgresql/data/base/16384/16400*
# 预期输出:
# 16400     (1GB)
# 16400.1   (1GB)
# 16400.2   (1GB)
# 16400.3   (1GB)
# 16400.4   (512MB)

# 步骤 2:使用 recover truncate 自动处理多段文件
mkdir -p /tmp/large_recover
fgpdu recover truncate -d /postgresql/data -o /tmp/large_recover -v

# 步骤 3:或使用 scan-file 直接扫描(自动处理多段)
fgpdu scan-file /postgresql/data/base/16384/16400 -o /tmp/large_recover -f sql -v

# 步骤 4:检查恢复结果
ls -la /tmp/large_recover/
wc -l /tmp/large_recover/16400_recovered.sql

5.8 案例 8:跨操作系统平台恢复

场景描述:生产服务器是 RHEL 7,需要在 Ubuntu 22.04 的恢复服务器上进行数据恢复。

操作步骤

bash 复制代码
# ============================================================
# 在 RHEL 7 生产服务器上(编译 FGPDU 或拷贝预编译版本)
# ============================================================

# 步骤 1:编译 FGPDU(或在其他机器编译后拷贝)
cd /path/to/FGPDU
make standalone
# 或
make portable

# 步骤 2:将 fgpdu 和数据目录拷贝到恢复服务器
scp build/portable/fgpdu recover@ubuntu-server:/tmp/
scp -r /postgresql/data recover@ubuntu-server:/tmp/pgdata_copy

# ============================================================
# 在 Ubuntu 22.04 恢复服务器上执行恢复
# ============================================================

# 步骤 3:赋予执行权限
chmod +x /tmp/fgpdu

# 步骤 4:验证 fgpdu 可运行
/tmp/fgpdu -V

# 步骤 5:诊断数据目录
/tmp/fgpdu diagnose -d /tmp/pgdata_copy

# 步骤 6:执行恢复
mkdir -p /tmp/recover_output
/tmp/fgpdu scan -d /tmp/pgdata_copy -o /tmp/recover_output -v --continue-on-error

# 步骤 7:检查恢复结果
ls -la /tmp/recover_output/

5.9 案例 9:Windows 平台恢复

场景描述:PostgreSQL 部署在 Windows Server 2019 上,数据目录因磁盘故障部分损坏,需要在 Windows 上直接恢复。

操作步骤

bat 复制代码
:: 步骤 1:停止 PostgreSQL 服务
net stop postgresql-15

:: 步骤 2:备份数据目录(使用 robocopy)
robocopy "C:\Program Files\PostgreSQL\15\data" "D:\backup\pgdata_backup" /E /COPYALL

:: 步骤 3:诊断数据目录
fgpdu.exe diagnose -d "C:\Program Files\PostgreSQL\15\data" -v

:: 步骤 4:列出数据库
fgpdu.exe list databases -d "C:\Program Files\PostgreSQL\15\data"

:: 步骤 5:全库导出
mkdir D:\fgpdu_export
fgpdu.exe scan -d "C:\Program Files\PostgreSQL\15\data" -o D:\fgpdu_export --continue-on-error

:: 步骤 6:恢复 DELETE 数据
fgpdu.exe recover delete -d "C:\Program Files\PostgreSQL\15\data" -o D:\recover

:: 步骤 7:扫描单个数据文件
fgpdu.exe scan-file "C:\Program Files\PostgreSQL\15\data\base\16384\16521" -o D:\recover_single

:: 步骤 8:查看导出结果
dir D:\fgpdu_export\*.sql

Windows 路径提示

  • FGPDU 自动识别正斜杠 / 和反斜杠 \,两种写法等价;
  • 路径含空格或中文时必须用双引号包裹;
  • 接受盘符路径(C:\...)和 UNC 路径(\\server\share)。

5.10 案例 10:RHEL 5 老系统部署

场景描述 :生产环境是 RHEL 5.11(glibc 2.5),FGPDU 在现代 Linux 编译后拷贝过去报 GLIBC_2.x not found 错误。

操作步骤

bash 复制代码
# 步骤 1:在任意现代 Linux 上交叉编译出 RHEL5 兼容二进制
make rhel5
# 产物:build/rhel5/fgpdu

# 步骤 2:验证不依赖高版本 glibc
file build/rhel5/fgpdu
readelf -s build/rhel5/fgpdu | grep GLIBC_ | head
# 预期:无 GLIBC_2.4+ 符号

# 步骤 3:拷贝到 RHEL5 服务器运行
scp build/rhel5/fgpdu root@rhel5-server:/usr/local/bin/
ssh root@rhel5-server '/usr/local/bin/fgpdu -V'

# 步骤 4:在 RHEL5 上执行恢复
ssh root@rhel5-server
/usr/local/bin/fgpdu diagnose -d /var/lib/pgsql/data -v
/usr/local/bin/fgpdu scan -d /var/lib/pgsql/data -o /tmp/recover --continue-on-error

如果要在 RHEL5 本机编译,需先安装:

bash 复制代码
# 挂载 RHEL5 DVD ISO 作为 yum 源
mount -o loop /dev/cdrom /mnt
yum install gcc make glibc-static
make rhel5

5.11 案例 11:国产化系统(麒麟/UOS)部署

场景描述:客户环境是银河麒麟 V10 SP3 / 统信 UOS V20,需要在国产系统上恢复 PostgreSQL 数据。

操作步骤

bash 复制代码
# 麒麟/UOS 通常基于 RHEL/CentOS,可直接使用 standalone 编译
cd /path/to/FGPDU
yum install gcc make glibc-static
make standalone

# 如果是 ARM 架构的麒麟系统(如飞腾/鲲鹏),暂不支持(仅支持 x86_64/i686)
# 验证架构
uname -m
# 预期输出:x86_64 或 i686

# 编译完成后拷贝到目标机器
scp build/standalone/fgpdu root@kylin-server:/usr/local/bin/

# 执行恢复
/usr/local/bin/fgpdu diagnose -d /postgresql/data -v
/usr/local/bin/fgpdu scan -d /postgresql/data -o /tmp/recover --continue-on-error

六、常用问题与排查

6.1 常见运行时错误

错误信息 原因 解决方案
Data directory not found 数据目录路径错误 检查路径是否正确,使用 -d 指定绝对路径
Not a valid PG data directory 目录不是合法的 PG 数据目录 确保目录包含 PG_VERSION 文件
pg_class not found 系统表损坏或 PG 版本差异 使用 scan-file 直接扫描数据文件
Permission denied 读权限不足 使用 postgres 用户运行,或 sudo
File for OID not found 数据文件已被 VACUUM 清除 尝试 recover drop 扫描孤立文件
Out of memory 系统内存不足 增加系统内存,或使用 scan-file 分批处理
No such file or directory 数据文件路径错误 使用 ls 确认文件存在
Segmentation fault 数据文件严重损坏 已通过信号处理捕获,使用 --continue-on-error

6.2 跨平台编译与运行排错

Q1: 在 RHEL 5 / CentOS 5 上运行报 GLIBC_2.x not foundsegmentation fault

原因 :使用 make standalone 编译的二进制可能引用了 RHEL5 glibc 2.5 不支持的符号。

解决:必须使用专用的 RHEL5 编译目标:

bash 复制代码
# 在任意现代 Linux 上交叉编译出 RHEL5 兼容二进制
make rhel5

# 验证不依赖高版本 glibc
file build/rhel5/fgpdu
readelf -s build/rhel5/fgpdu | grep GLIBC_ | head
# 预期:无 GLIBC_2.4+ 符号

# 拷贝到 RHEL5 服务器运行
scp build/rhel5/fgpdu root@rhel5-server:/usr/local/bin/
ssh root@rhel5-server '/usr/local/bin/fgpdu -V'

make rhel5 已通过编译期检测,在 gcc 4.1 环境下自动回退到 gettimeofday(),无需手动链接 librt。

Q2: 在 RHEL 6 / CentOS 6 上运行报 GLIBC 版本错误

解决:使用专用编译目标,兼顾新 ldflags 与旧 glibc 2.12:

bash 复制代码
make rhel6
# 产物:build/rhel6/fgpdu
Q3: 在 RHEL 7 / CentOS 7 上编译或运行异常

解决:使用专用编译目标(glibc 2.17,gcc 4.8+):

bash 复制代码
make rhel7
# 产物:build/rhel7/fgpdu
Q4: 如何在 Linux 上编译出 Windows 版 fgpdu.exe

前提:安装 MinGW-w64 交叉编译工具链。

bash 复制代码
# RHEL / Fedora / CentOS
yum install mingw64-gcc mingw32-gcc mingw64-binutils mingw32-binutils

# Ubuntu / Debian
apt-get install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 binutils-mingw-w64

# 编译 64 位 Windows EXE
make windows-mingw
# 产物:build/win64/fgpdu.exe

# 编译 32 位 Windows EXE(兼容 XP x86)
make windows-mingw32
# 产物:build/win32/fgpdu_x86.exe

验证

bash 复制代码
file build/win64/fgpdu.exe
# 预期:PE32+ executable (console) x86-64, for MS Windows

# 测试运行(如安装了 Wine)
wine build/win64/fgpdu.exe -V
Q5: 在 Windows 上运行 fgpdu.exe 报「找不到入口」或「缺少 DLL」

原因:如果使用 MSVC 动态编译,可能缺少 VC++ 运行时。

解决

  1. 推荐使用 make windows-mingw 生成的全静态 EXE(零 DLL 依赖)。

  2. 如需用 MSVC 编译,使用项目提供的 build\windows-msvc.bat(自动静态链接):

    bat 复制代码
    cd \path\to\FGPDU
    build\windows-msvc.bat
Q6: Windows 路径含空格或中文导致报错

解决 :路径用双引号包裹,FGPDU 自动兼容正斜杠 / 和反斜杠 \

bat 复制代码
:: 正确写法
fgpdu.exe diagnose -d "C:\Program Files\PostgreSQL\15\data"
fgpdu.exe scan -d "D:/PG数据/15/data" -o "E:/recover"

:: 错误写法(路径未加引号)
fgpdu.exe diagnose -d C:\Program Files\PostgreSQL\15\data
Q7: make windows-mingwx86_64-w64-mingw32-gcc: command not found

原因:未安装 MinGW-w64 交叉编译器。

解决

bash 复制代码
# RHEL / Fedora
yum install mingw64-gcc

# Ubuntu / Debian
apt-get install gcc-mingw-w64-x86-64

# 验证
x86_64-w64-mingw32-gcc --version
Q8: RHEL5 上 make rhel5clock_gettime 链接失败

原因 :RHEL5 的 glibc 2.5 将 clock_gettime 放在 librt 中,静态链接时可能找不到。

解决make rhel5 目标已通过编译期检测,在 gcc 4.1 环境下自动回退到 gettimeofday(),无需手动链接 librt。确保使用的是最新代码:

bash 复制代码
# 确认时间函数已回退
grep -n "gettimeofday" src/utils/pg_platform.c
# 预期输出包含 RHEL5 gcc-4.1 分支

6.3 数据恢复相关常见问题

Q1: 恢复后数据不完整?
  • 原因:VACUUM 已运行,物理删除了部分数据。
  • 解决 :尽快恢复,在 VACUUM 之前执行。可以禁用 autovacuum:ALTER TABLE orders SET (autovacuum_enabled = false);
Q2: pg_class 读不到?
  • 原因:系统表损坏或 PG 版本不匹配。
  • 解决 :使用 scan-file 直接扫描数据文件,绕过系统表。或直接执行 scan 命令,FGPDU 会自动降级为 EMERGENCY 紧急扫描模式。
Q3: 恢复的数据有乱码?
  • 原因:字符集不匹配。

  • 解决:确保新库的 encoding 与原库一致。查看原库 encoding:

    bash 复制代码
    pg_controldata /postgresql/data | grep LC_
Q4: 如何确认数据已删除?
  • 在 PG 中执行 SELECT count(*) FROM table;,对比 FGPDU 恢复的行数。
  • FGPDU 恢复的行数通常大于或等于 SELECT 结果(包含已删除但未 VACUUM 的行)。
Q5: 是否会修改原数据文件?
  • 不会。FGPDU 是只读操作,不会修改任何原始数据文件。可以放心反复尝试不同的恢复策略。
Q6: 提示 "File for OID not found" 怎么办?
  • 数据文件可能已被 VACUUM 或操作系统回收。
  • 尝试使用 recover drop 扫描孤立文件。
  • 或使用 scan-file 直接扫描 base 目录下的文件。
Q7: 诊断时显示 "pg_control file missing"?
  • pg_control 文件位于 global/pg_control
  • 如果丢失,FGPDU 将使用默认块大小(8192)。
  • 大多数情况下不影响数据恢复。
Q8: 大表恢复很慢怎么办?
  • 使用 --progress 查看进度。
  • 使用 -v 查看详细日志,定位慢的表。
  • 考虑将数据目录拷贝到本地 SSD 上再恢复。
  • 使用 scan-file 单独处理大表。
  • 使用并行脚本(参见 4.4.4 节)同时处理多张表。
Q9: 恢复的 SQL 文件导入报错?
  • 可能是表结构不匹配。

  • 先手动创建表结构,再导入数据:

    bash 复制代码
    psql -d mydb -c "CREATE TABLE orders (id int, name text, ...);"
    # 然后只导入 INSERT 语句部分
    grep "^INSERT" /tmp/export/public_orders.sql | psql -d mydb
  • 或修改 SQL 文件中的 CREATE TABLE 语句。

Q10: 如何在恢复前预览数据?
bash 复制代码
# 使用 scan-file 扫描并查看输出
fgpdu scan-file /postgresql/data/base/16384/16385 -o /tmp/preview -f sql
head -50 /tmp/preview/16385_recovered.sql
Q11: 恢复过程中出现 Segmentation fault
  • 这是数据文件严重损坏触发的信号。
  • FGPDU 已通过信号处理捕获,会输出提示信息后安全退出。
  • 使用 --continue-on-error 选项继续扫描。
  • 使用 scan-file 单独处理有问题的文件。
Q12: 如何处理 TOAST 表数据?
  • FGPDU 自动处理 TOAST 元组重组。
  • 当主表数据行过大被 TOAST 化时,FGPDU 会自动关联 TOAST 表并重组完整数据。
  • 无需手动干预。

6.4 跨平台验证清单

部署到目标平台后,按以下步骤验证 FGPDU 是否正常工作:

步骤 Linux 命令 Windows 命令 预期结果
1. 版本检查 ./fgpdu -V fgpdu.exe -V 显示版本号和平台信息
2. 帮助信息 ./fgpdu -h fgpdu.exe -h 显示完整命令列表
3. 诊断 PG 数据目录 ./fgpdu diagnose -d $PGDATA -v fgpdu.exe diagnose -d "C:\...\data" -v 输出目录结构检查结果
4. 列出数据库 ./fgpdu list databases -d $PGDATA fgpdu.exe list databases -d "C:\...\data" 显示数据库列表
5. 列出表 ./fgpdu list tables -d $PGDATA fgpdu.exe list tables -d "C:\...\data" 显示表列表
6. 全库导出 ./fgpdu scan -d $PGDATA -o /tmp/out fgpdu.exe scan -d "C:\...\data" -o D:\out 生成 .sql 文件
7. 恢复 DELETE 数据 ./fgpdu recover delete -d $PGDATA -o /tmp/r fgpdu.exe recover delete -d "C:\...\data" -o D:\r 生成恢复 SQL
8. 扫描单文件 ./fgpdu scan-file $PGDATA/base/16384/16521 -o /tmp/r fgpdu.exe scan-file "C:\...\data\base\16384\16521" -o D:\r 输出单表数据

提示 :在 RHEL 5/6/7 上部署时,建议先用 make rhel5 / make rhel6 / make rhel7 编译专用二进制,再按上表逐步验证。

6.5 恢复注意事项

  1. 尽快恢复 :数据删除后请尽快恢复,VACUUM 运行后数据将无法恢复。建议立即停止数据库或设置 autovacuum=off
  2. 只读操作:FGPDU 对数据目录是只读的,不会修改原始数据,可以放心反复尝试。
  3. 关闭数据库:建议在 PostgreSQL 停止状态下使用,避免数据不一致和 autovacuum 干扰。
  4. 备份优先 :使用前强烈建议备份原始数据目录(cp -arsync),即使在备份上操作。
  5. 权限要求 :需要对 PG 数据目录有读权限(通常 postgres 用户或 root)。
  6. 输出目录空间:确保输出目录有足够空间(至少数据目录大小的 1.5 倍)。
  7. 字符集一致:新库的字符集和 LC_* 设置应与原库一致,避免乱码。

七、附录

7.1 命令速查表

bash 复制代码
# 基本命令
fgpdu -V                              # 查看版本
fgpdu -h                              # 查看帮助
fgpdu diagnose -d PGDATA              # 诊断数据目录
fgpdu diagnose -d PGDATA -v           # 诊断(详细模式)

# 列表命令
fgpdu list tables -d PGDATA           # 列出所有表
fgpdu list tables -d PGDATA -s public # 列出指定 schema 的表
fgpdu list tables -d PGDATA -t orders # 查找指定表名
fgpdu list databases -d PGDATA        # 列出数据库
fgpdu list schemas -d PGDATA          # 列出 Schema

# 数据导出
fgpdu scan -d PGDATA -o DIR           # 导出全部(SQL)
fgpdu scan -d PGDATA -o DIR -f csv    # 导出全部(CSV)
fgpdu scan -d PGDATA -o DIR -f dmp    # 导出全部(DMP)
fgpdu scan -d PGDATA -t public.orders -o DIR        # 导出指定表
fgpdu scan -d PGDATA -u postgres -o DIR             # 按用户名导出
fgpdu scan -d PGDATA -s public -o DIR               # 按 schema 导出
fgpdu scan -d PGDATA -u mydb -s public -t orders -o DIR  # 精确导出

# 数据恢复
fgpdu recover delete -d PGDATA -o DIR             # 恢复 DELETE 数据
fgpdu recover delete -d PGDATA -t public.orders -o DIR  # 恢复指定表 DELETE
fgpdu recover delete -d PGDATA -u mydb -s public -o DIR # 恢复指定用户/schema DELETE
fgpdu recover drop -d PGDATA -o DIR               # 恢复 DROP 表数据
fgpdu recover drop -d PGDATA -u mydb -o DIR       # 恢复指定用户 DROP
fgpdu recover truncate -d PGDATA -o DIR           # 恢复 TRUNCATE 表数据
fgpdu recover truncate -d PGDATA -u mydb -o DIR   # 恢复指定用户 TRUNCATE

# 文件扫描
fgpdu scan-file FILE -o DIR          # 扫描单个文件
fgpdu scan-file FILE -o DIR -f sql   # 扫描并输出 SQL
fgpdu scan-file FILE -o DIR -f dmp   # 扫描并输出 DMP
fgpdu scan-file FILE -o DIR -v       # 详细模式

# 高级选项
fgpdu scan -d PGDATA -o DIR -v       # 详细日志
fgpdu scan -d PGDATA -o DIR --progress  # 显示进度
fgpdu scan -d PGDATA -o DIR --continue-on-error  # 遇错继续(自适应扫描)

7.2 性能建议

  1. 内存:大数据集建议 8GB+ 内存。
  2. 存储:输出目录使用 SSD 可大幅提升性能。
  3. 网络:拷贝到本地磁盘后再恢复,避免网络 I/O 瓶颈。
  4. 并行:可通过脚本并行处理多个表(参见 4.4.4 节)。
  5. 过滤 :使用 -t 指定表名,避免不必要的全量扫描。
  6. 格式选择:大规模恢复时优先使用 DMP 格式(速度最快),需要可读性时使用 SQL 格式。

7.3 目录结构

复制代码
FGPDU/
├── include/           # 头文件
│   ├── fgpdu.h       # 主头文件(类型/常量/错误码)
│   ├── page/         # 页面解析
│   ├── tuple/        # 元组解析
│   ├── io/           # IO 引擎
│   ├── dbdict/       # 系统表字典
│   ├── scan/         # 扫描器
│   ├── output/       # 输出器
│   ├── cli/          # 命令行接口
│   ├── utils/        # 工具函数
│   └── version/      # 版本兼容
├── src/              # 源代码
├── build/            # 构建输出
├── docs/             # 文档目录
├── Makefile          # 构建脚本
├── build_standalone.sh  # 独立编译脚本
├── README.md         # 说明文档
├── USAGE.md          # 使用手册
└── TESTDOC.md        # 测试手册

7.4 作者信息

联系方式 信息
作者 风哥
官方网站http://www.fgedu.net.cn , http://www.itpux.com
数据库教程https://edu.51cto.com/lecturer/8020378.html

7.5 版本历史

  • v1.0.7 (2026-08):跨平台扩展,同时支持 Linux 与 Windows;RHEL 5/6/7 专用编译目标;新增 Windows(XP~11,x86 & x64)原生支持。
  • v1.0.6 (2026-08):紧急全库扫描;坏块跳过机制;数据完整性校验;恢复步骤导出;内存安全加固。
  • v1.0.5 (2026-08):增强恢复能力,多段文件支持,自适应扫描,元组校验。
  • v1.0.4 (2026-08):最大数据恢复策略,原始元组扫描器,所有扫描循环改为 continue 而非 break。
  • v1.0.3 (2026-08):稳定性加固,添加信号处理,修复多个指针越界问题。
  • v1.0.2 (2026-08):新增按用户名/数据库名精确恢复功能,三维度组合过滤。
  • v1.0.1 (2026-08):修复 PG_VERSION 解析错误,添加自动检测数据块大小功能。
  • v1.0.0 (2025-08):初始版本发布,支持 PG for RHEL 5~10,实现 DELETE/DROP/TRUNCATE 恢复。
相关推荐
智购科技智能售货柜44 分钟前
2026自动售货机峰值交易流量应对方案:从消息削峰到弹性扩容的工程实践~YH
数据库·人工智能·单片机·嵌入式硬件·yolo
像风一样自由20201 小时前
13.pgvector入门用PostgreSQL直接实现向量检
人工智能·postgresql·大模型·rag·智能体
JavaPub-rodert1 小时前
Ontop 详解:不搬数据库,也能把 MySQL / PostgreSQL 变成知识图谱
数据库·mysql·postgresql
xiaoxiang96091 小时前
Git 分支同步实战:`merge -X theirs` 与完全合并方案
数据库·git·elasticsearch
GoppViper2 小时前
用户测试如何提升SEO转化率?四种实操方法与案例解析
数据库·经验分享
先吃饱再说2 小时前
后端开发绕不开的 SQL:从建表、查询到索引优化,一篇讲透
数据库·后端·sql
敲代码的小小酥2 小时前
InfluxDB时序数据库(续)
数据库·时序数据库
智购科技自动贩卖机3 小时前
自动售货机嵌入式系统Go语言开发实践:从资源受限设备到RTOS协程调度的工程化之路
大数据·linux·数据库·人工智能·yolo·架构·golang
SelectDB3 小时前
2026 年 Apache Doris 和 StarRocks 怎么选?一篇更接近真实选型的对比
大数据·数据库·数据分析