> **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 数据页格式(`PageHeaderData`、`ItemIdData`、`HeapTupleHeaderData`),将页面中的元组(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_database`、`pg_class`、`pg_namespace`、`pg_attribute`、`pg_tablespace` 等系统表,建立”数据库 OID ↔ 数据库名”、”relfilenode OID ↔ 表名 + Schema + 列定义”的映射字典。
3. **数据页解析层**:根据 PG 版本对应的页面格式(page version 1~4)解析每个 8KB 数据页:解析页头 `PageHeaderData`、`ItemId` 数组、`HeapTupleHeaderData`,提取元组的 `t_xmin` / `t_xmax` / `t_infomask` / `t_infomask2` 等事务信息,并定位元组数据区。
4. **元组数据提取层**:根据 `pg_attribute` 中记录的列定义(`atttypid`、`attlen`、`attalign`、`attstorage`、`atttypmod`、`attnotnull`)按 PG 内部对齐规则解析每列的值,处理定长/变长类型、NULL 位图、TOAST 外置数据、压缩数据、枚举类型、复合类型、数组类型等。
5. **输出层**:将解析出的元组按指定格式(SQL/CSV/TXT/DMP/Binary)写入到输出目录,并在 SQL 文件末尾自动追加”恢复步骤说明 + 数据校验 SQL”,方便用户直接复制执行完成后续恢复。
### 1.5 作者信息
| 联系方式 | 信息 |
|———|——|
| **作者** 风哥
| **WX** itpux-com
| **QQ** 113257174
| **官方网站** : http://www.fgedu.net.cn , http://www.itpux.com
| **数据库教程** : https://edu.51cto.com/lecturer/8020378.html
—
## 二、功能特性
### 2.1 离线数据抽取能力
FGPDU 最核心的能力是**完全脱离 PostgreSQL 服务进程**完成数据抽取。具体表现为:
– 不需要 `postgresql.conf`、`pg_hba.conf` 完整可用;
– 不需要 `pg_control` 完整可用(缺失时自动使用默认 8192 字节块大小);
– 不需要 WAL 日志完整可用;
– 不需要 `shared_buffers`、`work_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 工具导入或异构数据库迁移 | 高 | `\copy` 或 `COPY … 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_lower` 与 `pd_upper` 之间)中残留的已删除/覆盖元组数据,能恢复一些”看起来已经被覆盖”的数据。
启用 `–continue-on-error` 选项(默认启用)即可激活自适应扫描。遇到坏块时自动跳过继续扫描后续块,不会因为单个坏块中断整个恢复流程。
### 2.6 紧急全库扫描(EMERGENCY 模式)
当 `pg_class` / `pg_database` 等系统目录损坏或数据库完全无法启动时,FGPDU 会自动降级为**文件级紧急扫描**:
– 直接遍历 `base/` 目录下所有数据库子目录;
– 对每个数据库目录下的所有数据文件执行扫描;
– 自动识别真正的 PG 数据文件,跳过 `_fsm`(free space map)、`_vm`(visibility map)、`_init`、`PG_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_xmax` 与 `t_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_buf[8KB]、tuples[12KB]、items[2KB])改为动态内存分配,彻底消除栈溢出风险;
– **缓冲区溢出保护**:所有 `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 TABLE` 和 `INSERT 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 found` 或 `segmentation 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-mingw` 报 `x86_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 rhel5` 报 `clock_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 -a` 或 `rsync`),即使在备份上操作。
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 作者信息
| 联系方式 | 信息 |
|———|——|
| **作者** 风哥
| **WX** itpux-com
| **QQ** 113257174
| **官方网站** : 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 恢复。
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
