首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >PG 数据库无法启动如何恢复?

PG 数据库无法启动如何恢复?

作者头像
用户4035096
发布2026-07-10 11:10:28
发布2026-07-10 11:10:28
30
举报

生产库最难处理的事故,不是“有备份、能按流程恢复”的事故,而是介于数据库内核、文件系统、WAL 归档和业务止损之间的灰色地带:实例起不来、系统目录损坏、误删误改刚发生、单个数据文件丢失、PITR 回到某个时间点会牵连大量正常业务写入。

怎么办?来看看PDU!

PDU(PostgreSQL Data Unloader)的价值,不在于替代备份体系,而在于给 PostgreSQL DBA 多一个“数据库不必启动、原始文件只读、尽量定向导出”的救援通道。

背景:PostgreSQL 的恢复体系很强,但不是所有事故都适合整库回滚

PostgreSQL 官方文档说明,WAL 记录数据文件的每次变更,数据库崩溃后可通过重放 WAL 恢复一致性;连续归档和 PITR 则通过文件系统级备份加 WAL 归档,把数据库恢复到某个时间点。这是主干恢复体系,必须优先建设。

问题在于,PITR 的恢复粒度通常是实例或集群级,需要准备替代环境、选择恢复目标点、处理时间线分叉,并在恢复后做数据比对和业务补偿。对于“只误删了一张表的一部分数据”“实例系统目录损坏但很多用户表文件仍可读”“需要先抢出核心表再慢慢重建”的场景,整库恢复经常过重。

PDU 切入的是另一个面:不从 SQL 执行器进入数据库,而是直接面对 PostgreSQL 数据目录、关系文件、TOAST 和 WAL 归档。它更像 PostgreSQL 领域的“离线数据卸载器”,适合在常规路径失败或成本过高时救急。

观点:PDU 的定位应该是“备份恢复体系的补充工具”,不是备份替代品。

成立前提:团队已经有基础备份、归档和恢复演练;PDU 被纳入灾备工具箱,而不是灾后第一次下载。

场景:谁会真正需要 PDU

PDU 面向的不是日常 ETL,而是事故中的 DBA、数据库内核支持工程师、平台 SRE、安全取证人员和业务系统负责人。

典型触发场景:

场景

症状

PDU 为什么可能有用

关键前提

实例无法启动

catalog、控制文件、部分关系文件异常

绕过数据库引擎,直接读数据文件导出

用户表文件仍可读

误 DELETE

业务行被删除,应用已提交

扫描 WAL 归档,提取删除前记录

WAL 覆盖事故窗口

误 UPDATE

大量字段被错误覆盖

从 WAL 找回更新前值

WAL 记录仍可解析

单表数据文件损坏

一张表打不开或部分块坏

逐页读取可解析块,尽量导出可见元组

损坏不是全文件级

取证分析

不希望启动原库或写入原盘

只读扫描 PGDATA

有一致的文件副本

痛点:传统做法不是不能用,而是粒度和现场成本太高

第一类传统方案是 PITR。它是 PostgreSQL 正统恢复能力,但在误删少量数据时,PITR 会把整个数据库带回过去某个时间点。后续要把误删数据从恢复环境搬回生产,还要处理事故后正常产生的业务写入。

第二类工具是 pg_waldump。官方文档定义它用于把 WAL 做成人类可读形式,主要适合调试或学习。它能帮助理解 WAL,但不会自动把误删行重建成业务可直接导入的 CSV/SQL。

第三类工具是 pg_filedump。PostgreSQL wiki 和项目 README 都把它定位为格式化查看 heap/index/control 文件的低层工具。它适合看页面结构、定位损坏,但不是面向 DBA 的一站式恢复工作流。

第四类工具是 pg_dirtyread。它能读取 dead but unvacuumed tuples,适合误删后 VACUUM 前的窗口;但它要求数据库能启动、能安装扩展、目标旧版本元组还没有被清理。

观点:PDU 的批判对象不是 PITR、pg_waldump、pg_filedump 或 pg_dirtyread 本身,而是“在所有事故中只会使用单一路径”的恢复策略。

成立前提:事故恢复目标是尽快缩小损失面,而不是严格回放整个实例到一致时间点。

产品方案:PDU 做了什么

PDU 的核心方案可以概括为四步:

  1. PGDATA 读取 PostgreSQL 系统目录相关文件,建立数据库、schema、table、attribute、type 等离线元数据。
  2. 根据元数据定位表的数据文件,按 PostgreSQL page 和 tuple 格式读取 heap 数据。
  3. 对字段类型做解码,处理常见数值、时间、文本、UUID、网络地址、JSON 和 TOAST 数据,并导出为 CSV 或 SQL。
  4. 在误删误改场景中扫描 WAL 归档,恢复 DELETE 行或 UPDATE 前值。

README 给出的最小使用路径是:修改 basic.h 中的 PG_VERSION_NUM,执行 make,配置 pdu.ini 中的 PGDATAARCHIVE_DEST,启动 ./pdu,然后执行 b; 初始化元数据,再通过 useset\dt\d+unloadscanrestore 完成恢复。

架构与实现原则

PDU 是一个 C 语言命令行工具,核心文件包括:

模块

作用

证据

pdu.c

CLI 入口、命令解析、批量命令执行

parseCmd 识别 b/use/set/unload/scan/dropscan/info/restore/meta 等命令,main 支持交互和命令行模式

read.c

元数据引导、关系文件读取、命令分发

包含 execCmd、readItems、bootDBStruct、bootAttrStruct、bootTabStruct、bootSCHStruct 等函数

decode.c

字段解码、类型处理、TOAST 相关上下文

包含 typeHandlerRegistry,注册 int、float、numeric、time、date、timestamp、uuid、bool、macaddr、varchar 等处理器

dropscan_fs.c

文件系统/删除扫描相关能力

仓库文件名和 README 的数据文件丢失场景相互印证

pg_xlogreader.c、pg_walgettx.c

WAL 读取和事务恢复相关能力

README 的 WAL Archive Scanning、scan、restore del/upd 命令与这些模块对应

PDU 的架构不是“连接 PostgreSQL 执行 SQL”,而是“用 PostgreSQL 存储格式知识重建一个最小离线读取器”。

前后效果对比

维度

只有传统恢复路径

加入 PDU 后的补充路径

实例无法启动

依赖备份、PITR、物理修复

可尝试直接从可读关系文件导出

误删少量数据

恢复副本后人工搬数据

有 WAL 时可定向扫描和导出

工具学习曲线

多个工具分别掌握

一个交互式工具覆盖多个救援动作

原盘风险

修复动作可能需要写入环境

README 声明只读读取原始数据文件

性能承诺

取决于备份和恢复环境

README 明确 PDU 对大表执行全表扫描;没有公开基准时不应宣称固定性能收益

可审计性

标准 PITR 更成熟

PDU 需要团队自建演练记录和输出校验流程

竞品与替代方案比较

工具/方案

核心机制

适合场景

不适合场景

与 PDU 的关系

PostgreSQL PITR

基础备份 + WAL 归档重放

严肃生产恢复、整库时间点恢复

只找回少量行时成本偏高

主恢复体系,PDU 不应替代

pg_waldump

人类可读 WAL 展示

调试、学习、分析 WAL

自动恢复业务行

PDU 把 WAL 分析向恢复动作推进

pg_filedump

格式化查看 heap/index/control 文件

页面级诊断、低层检查

面向业务表批量导出

PDU 更偏恢复工作流

pg_dirtyread

SQL 扩展读取未清理 dead tuple

实例可启动、VACUUM 前误删找回

实例无法启动、dead tuple 已清理

PDU 覆盖离线和 WAL 路径

pgBackRest/Barman

企业级备份、归档、恢复管理

标准灾备、自动化、审计

无备份或只需离线提取局部数据

应与 PDU 组合,而不是二选一

观点:PDU 最应该比较的不是 pgloader、pg_bulkload 这类数据加载工具,而是 PITR、pg_waldump、pg_filedump、pg_dirtyread 这些事故恢复/诊断路径。

成立前提:读者关心的是 PostgreSQL 原生数据文件和 WAL 中的“救援”,不是跨库迁移或高吞吐导入。

使用场景和操作要点

场景一:实例损坏但数据文件仍可读

症状:PostgreSQL 启动失败,业务要求先抢出核心表。

为什么 PDU 有用:它不依赖运行中的数据库实例,先通过 b; 从 PGDATA 引导元数据,再 unload 指定表或 schema。

命令:

代码语言:javascript
复制
PDU> b;
PDU> use mydb;
PDU> set public;
PDU> unload customers;

预期信号:当前目录下生成对应 CSV 文件;日志显示成功和失败记录数。

限制:如果系统目录、表文件、TOAST 文件同时严重损坏,导出可能不完整。

场景二:误删订单数据

症状:错误 DELETE 已提交,需要找回删除前行。

为什么 PDU 有用:README 声明可扫描 WAL 归档提取已删除行。

命令:

代码语言:javascript
复制
PDU> use production;
PDU> set public;
PDU> scan orders;
PDU> restore del orders;

预期信号:删除记录导出为 CSV。

限制:ARCHIVE_DEST 必须指向正确 WAL 归档目录,且归档覆盖误删时间窗口。

场景三:误更新用户字段

症状:错误 UPDATE 覆盖了关键字段。

命令:

代码语言:javascript
复制
PDU> scan users;
PDU> restore upd users;

预期信号:UPDATE 前值被导出。

限制:需要 WAL 中包含相关 UPDATE 记录;复杂类型或未支持类型可能无法完整还原。

场景四:数据文件部分损坏

症状:单个表文件部分页损坏。

为什么 PDU 有用:read.creadItems 逻辑按页读取并对 tuple 解码;这类设计天然适合“能读多少导多少”的抢救思路。

命令:

代码语言:javascript
复制
PDU> b;
PDU> use damaged_db;
PDU> set public;
PDU> unload critical_table;

限制:这是“尽量抢救”,不是一致性恢复。导出后必须做行数、主键、业务约束和抽样校验。

最小上手步骤

以下命令来自 README,并按生产演练做了少量上下文补充。

1. 安装依赖

Ubuntu/Debian:

代码语言:javascript
复制
sudo apt-get update
sudo apt-get install build-essential liblz4-dev zlib1g-dev

RHEL/CentOS:

代码语言:javascript
复制
sudo yum install gcc lz4-devel zlib-devel

2. 设置目标 PostgreSQL 主版本并编译

代码语言:javascript
复制
sed -i 's/#define PG_VERSION_NUM [0-9]\+/#define PG_VERSION_NUM 16/g' basic.h
make clean
make

README 的模板写的是把 PG_VERSION_NUM 替换为 14-18 的目标版本。这里用 16 只是示例。

3. 配置 pdu.ini

代码语言:javascript
复制
PGDATA=/var/lib/postgresql/15/main
ARCHIVE_DEST=/var/lib/postgresql/wal_archive

PGDATA 用于离线读取数据目录;ARCHIVE_DEST 用于 DELETE/UPDATE 的 WAL 扫描恢复。

4. 启动并初始化

代码语言:javascript
复制
./pdu
代码语言:javascript
复制
PDU> b;
PDU> \l;
PDU> use production_db;
PDU> set public;
PDU> \dt;
PDU> \d+ customers;
PDU> unload customers;

5. 验证导出结果

建议至少做四类校验:

校验

方法

文件完整性

检查 CSV/SQL 文件大小、行尾、编码

行数

与备份副本、业务账表、审计日志比对

主键重复

导入临时库后检查主键/唯一键

业务抽样

按订单号、用户号、时间范围抽查

6. 回滚与清理

PDU 自身按 README 声明只读原始数据文件;清理主要是删除临时导出的 CSV/SQL、日志和临时验证库。不要在原 PGDATA 上做写入式修复实验,建议先复制数据目录或挂载存储快照。

最佳实践

  1. 在事故前演练。至少准备 PostgreSQL 14-18 中本团队使用版本的测试集,覆盖普通表、TOAST 大字段、误删、误改、归档缺失、数据文件损坏。
  2. 固定版本和二进制。PDU 编译时依赖 PG_VERSION_NUM,不要在事故现场临时猜版本。
  3. 永远从副本或快照读取。即使工具只读,也应避免在原盘上做任何额外风险动作。
  4. 保留完整证据链。记录 PGDATA 来源、WAL 范围、PDU commit 或 release、命令、输出日志、导出文件校验和。
  5. 和 PITR 联合使用。PDU 导出的是救援数据,不等于恢复了数据库一致时间线;关键数据应先进临时库校验,再通过业务流程回补。
  6. 控制权限。只有 DBA/平台管理员可接触 PGDATA 和 WAL;导出的 CSV/SQL 很可能包含敏感数据,需要加密存放并限制流转。
  7. 监控归档。PDU 的误删误改恢复依赖 WAL 归档,归档失败会直接击穿这个前提。PostgreSQL 官方文档也提醒归档命令反复失败会导致 pg_wal 持续增长,甚至文件系统满后数据库 PANIC。

风险、边界和失败条件

PDU 不是魔法。它的边界比宣传语更重要。

风险

影响

应对

类型支持有限

README 列出 enum、composite、range、tsvector/tsquery 不支持

事故前做类型盘点;复杂类型走 PITR 或应用侧重建

版本格式不匹配

页面/tuple/WAL 解析错误

按 PostgreSQL 主版本编译和演练

WAL 不完整

DELETE/UPDATE 恢复失败

监控 archive_command,保留足够 WAL

数据文件物理损坏严重

只能部分导出或无法导出

使用存储快照、块设备镜像、专业物理恢复

无公开基准

不能承诺恢复耗时

用本地数据规模做演练基准

DeepWiki 证据缺失

架构结论需谨慎

本文已将架构部分标记为源码推断

观点:PDU 应在“可读文件 + 可用 WAL + 可验证输出”的边界内使用。

成立前提:恢复团队愿意把导出数据再经过临时库校验和业务比对。

结论

PDU 最值得重视的地方,是它把 PostgreSQL 灾难恢复中的几个低层动作串成了一个相对统一的交互式工具:读 PGDATA 建元数据、读 heap/TOAST 导出、扫 WAL 找回误删误改前的数据。它不会取代 PITR,也不应该取代备份软件;它应该被放在“常规恢复太重、实例无法启动、但底层文件仍有价值”的位置上。

对 DBA 和架构师的实际建议:

  1. 把 PDU 纳入灾备演练,而不是等事故发生后才研究。
  2. 对核心库做类型、TOAST、归档覆盖、表规模的演练矩阵。
  3. 用 PITR 保底,用 PDU 缩小恢复粒度,用临时库校验结果,用业务流程完成回补。
  4. 如果文件/WAL/类型/版本任一前提不满足,立即切换到标准备份恢复或专业数据恢复路径。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 digoal德哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 生产库最难处理的事故,不是“有备份、能按流程恢复”的事故,而是介于数据库内核、文件系统、WAL 归档和业务止损之间的灰色地带:实例起不来、系统目录损坏、误删误改刚发生、单个数据文件丢失、PITR 回到某个时间点会牵连大量正常业务写入。
  • 怎么办?来看看PDU!
    • 背景:PostgreSQL 的恢复体系很强,但不是所有事故都适合整库回滚
    • 场景:谁会真正需要 PDU
    • 痛点:传统做法不是不能用,而是粒度和现场成本太高
    • 产品方案:PDU 做了什么
    • 架构与实现原则
    • 前后效果对比
    • 竞品与替代方案比较
    • 使用场景和操作要点
      • 场景一:实例损坏但数据文件仍可读
      • 场景二:误删订单数据
      • 场景三:误更新用户字段
      • 场景四:数据文件部分损坏
    • 最小上手步骤
      • 1. 安装依赖
      • 2. 设置目标 PostgreSQL 主版本并编译
      • 3. 配置 pdu.ini
      • 4. 启动并初始化
      • 5. 验证导出结果
      • 6. 回滚与清理
    • 最佳实践
    • 风险、边界和失败条件
    • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档