上一篇我们聊了索引的 B-tree 结构和失效场景。这一篇,我们进入运维中最不能心存侥幸的环节:备份。逻辑备份是 PostgreSQL 日常运维中使用频率最高的备份方式,但它也是最容易被"想当然"对待的------很多人以为跑通了
pg_dump就等于有了备份,直到真正需要恢复时才发现文件损坏、对象缺失、或者根本不知道该用哪个工具恢复。这一篇,我们把pg_dump、pg_dumpall、pg_restore三个工具的分工、四种格式的选择、并行备份的边界,以及"备份文件如何验证可恢复"这个核心问题,逐一拆开。
一、逻辑备份到底在备份什么?
逻辑备份的本质是:通过数据库连接读取对象定义和数据,输出为 SQL 脚本或归档文件 。它不复制任何物理数据文件,而是重新"描述"数据库------建表语句、数据 COPY 流、索引和约束定义。
这个本质决定了逻辑备份的两个核心特征:
第一,它是一致性快照,不是锁表。 pg_dump 利用 PostgreSQL 的 MVCC 机制,在启动时获取一个一致性快照,整个备份过程基于这个快照读取数据,备份期间对数据库的更新不会进入备份文件,业务读写也不会被阻塞 。你不需要在业务低峰期执行 pg_dump,但需要知道:备份文件反映的是"备份启动那一刻"的状态,而不是"备份结束那一刻"。
第二,它不包含 WAL,无法做 PITR。 逻辑备份没有时间点恢复能力,无法恢复到"某个具体时刻之前"的状态。这是它和物理备份最根本的能力边界。
二、三个工具的分工:一张表说清
很多人混淆 pg_dump 和 pg_dumpall,核心原因是没分清"单库"和"集群全局对象"的边界。
| 工具 | 备份范围 | 是否包含角色/表空间 | 输出格式 |
|---|---|---|---|
pg_dump |
单个数据库 | 不包含 | 全部四种格式 |
pg_dumpall |
整个集群(所有库 + 全局对象) | 包含 | 仅 plain 文本 |
pg_restore |
--- | --- | 恢复非 plain 归档(custom/directory/tar) |
关键规则:pg_dump 不备份角色(roles)和表空间(tablespaces)定义。 如果你用 pg_dump 备份了业务库,恢复到新集群后,表可能无法正常访问,因为角色不存在、表空间路径缺失。
正确的集群级迁移策略是两步走 :先用 pg_dumpall --globals-only 导出全局对象(角色、表空间、授权),再对每个数据库分别执行 pg_dump。不要直接用 pg_dumpall 导出全部数据------它只能生成 plain 文本,不支持并行,大集群下恢复速度极慢。
三、四种格式:plain、custom、directory、tar
pg_dump 支持四种输出格式,选择格式直接决定了你"能不能并行、能不能选择性恢复"。
plain(-Fp):纯 SQL 脚本
输出可读的 SQL 文本,用 psql -f 恢复。优点是可编辑、可审查、跨版本兼容性好 ;缺点是无法使用 pg_restore 选择性恢复,大库恢复慢,且不支持并行。适合小库、需要人工审查的场景。
custom(-Fc):生产最常用
二进制归档,默认压缩 ,支持 pg_restore 进行选择性恢复(单 schema、单表、单扩展)和并行恢复 ,但不支持并行备份。这是日常单库备份的推荐默认格式。
directory(-Fd):唯一支持并行备份的格式
输出为一个目录,内部按表和段拆分成多个文件,唯一支持 -j N 并行备份的格式,同时支持并行恢复。大库备份的首选。
tar(-Ft):兼容但受限
tar 归档,不支持压缩,选择性恢复能力有限。实际生产中很少作为首选。
格式选择的一句话原则: 日常单库 → -Fc;大库追求备份速度 → -Fd -j N;需要可读 SQL 做审查 → -Fp;-Ft 基本可以忽略。
四、并行备份与并行恢复:边界必须搞清楚
这是最容易踩坑的地方。
并行备份只属于 directory 格式。 pg_dump -Fd -j 4 -f /backup/mydb_dir 会用 4 个并发连接同时导出不同表的数据。custom 格式不支持 -j,即使你写了 pg_dump -Fc -j 4,-j 也会被忽略或报错。
并行恢复 custom 和 directory 都支持。 pg_restore -j 4 -d target_db mydb.dump 可以并行加载数据。
所以大库的典型组合是:pg_dump -Fd -j 8 并行备份,pg_restore -j 8 并行恢复。custom 格式则只能"串行备份、并行恢复"。如果备份窗口本身就是瓶颈,directory 是唯一解。
五、pg_restore 的段拆分:为什么恢复速度能快起来
非 plain 格式的归档内部被拆分为三个段:pre-data → data → post-data。
- pre-data:表 DDL、类型、函数(不含索引、外键、触发器);
- data :
COPY数据流、序列值、大对象内容; - post-data:索引、外键、触发器、约束、规则。
这个拆分让恢复可以按"先建表 → 并行灌数据 → 最后建索引 "的顺序执行。post-data 中的索引在数据加载完成后一次性构建,比逐行维护索引快得多。这也是为什么 pg_restore 恢复大库时,即使不开并行,也通常比 psql -f 执行 plain SQL 更快------plain 脚本中的索引语句会在数据加载过程中逐步执行,每次插入都要维护 B-tree。
pg_restore 还支持选择性恢复:-t table_name 只恢复某张表,-n schema_name 只恢复某个 schema,-l 列出归档内容先确认对象清单。这是逻辑备份相比物理备份的核心优势之一------物理备份只能整体恢复,逻辑备份可以只捞回一张表。
六、备份文件如何验证"可恢复"?
这是本篇最关键的实操问题。生成了备份文件,不等于拥有了一份可用的备份。
验证分三个层次,成本递增,可信度也递增。
第一层:文件可读性检查
bash
# custom/directory/tar:列出归档目录,确认 TOC 可读
pg_restore --list mydb.dump > /dev/null
# plain:语法检查
psql --set=ON_ERROR_STOP=on -f mydb.sql --dry-run # 部分版本支持
pg_restore -l 能正常输出对象清单,说明归档结构完整,文件没有在传输或存储中损坏。
第二层:恢复到测试库,跑冒烟查询
这是最基本的可靠验证。创建一个临时数据库,用 pg_restore --exit-on-error 恢复,然后对核心表执行 COUNT(*) 或业务关键查询,与源库对比。
bash
createdb -h restore_host -U postgres mydb_verify
pg_restore --exit-on-error -h restore_host -U postgres -d mydb_verify mydb.dump
psql -h restore_host -U postgres -d mydb_verify -c "SELECT count(*) FROM orders;"
--exit-on-error 让 pg_restore 在第一个错误时立即退出,而不是继续"带病恢复"。没有这个参数,恢复可能"成功"结束,但部分对象缺失,等到业务上线才发现问题。
第三层:自动化恢复演练
生产环境应该把"恢复到测试库"做成定时任务,而不是靠人工偶尔想起。社区中的实践是:备份完成后自动触发恢复脚本,恢复完成后自动对比关键表的行数,不一致则告警。备份的可信度不来自"备份成功"的日志,而来自"恢复成功且数据一致"的验证。
一个容易被忽略的细节:物化视图的数据不会被 pg_dump 导出,只导出定义 。恢复后物化视图是空的,状态为未填充,必须手动执行 REFRESH MATERIALIZED VIEW。如果你的备份验证发现某个"表"的行数为零,先确认它是不是物化视图。
七、逻辑备份 vs 物理备份:怎么选
| 维度 | 逻辑备份(pg_dump) | 物理备份(pg_basebackup + WAL) |
|---|---|---|
| 备份单位 | 库 / schema / 表 | 整个数据目录 |
| 跨版本兼容 | ✅ 跨大版本、跨架构 | ❌ 版本必须严格一致 |
| 选择性恢复 | ✅ 单表、单 schema | ❌ 只能整体恢复 |
| 恢复速度 | 较慢 | 快 |
| PITR | ❌ 不支持 | ✅ 支持 |
| 对业务影响 | 不锁表,基于快照 | 依赖 WAL 归档,可在线 |
| 适合场景 | 迁移、环境克隆、误删表找回、小库日常备份 | 大库灾备、快速全量回滚、时间点恢复 |
两者不是替代关系,而是互补关系。 逻辑备份负责"灵活性"------跨版本、单对象、迁移;物理备份负责"速度和完整性"------大库快速恢复、PITR。生产环境的成熟策略通常是:物理备份做灾备底座,逻辑备份做细粒度兜底和迁移工具。
八、总结
| 主题 | 核心结论 |
|---|---|
| 一致性 | MVCC 快照,不锁表,备份期间更新不入备份 |
| 工具边界 | pg_dump 单库、不含角色/表空间;pg_dumpall 集群、仅 plain |
| 格式选择 | custom 日常首选;directory 唯一支持并行备份;plain 可读但恢复慢 |
| 并行边界 | 并行备份仅 directory;并行恢复 custom/directory 都支持 |
| 恢复拆分 | pre-data → data → post-data,索引最后建 |
| 验证 | 至少恢复到测试库 + --exit-on-error + 行数对比;物化视图需手动 REFRESH |
| 选型 | 逻辑与物理互补,不是替代 |
一句话:逻辑备份的价值不在于"备份"这个动作,而在于"恢复"这个能力。没有经过恢复验证的备份文件,只是一堆占空间的字节。
下一篇,我们进入物理备份:pg_basebackup 与 PITR------为什么物理备份能做时间点恢复?pg_basebackup 和 WAL 归档如何配合?
思考题:
你平时用 pg_dump 备份时,默认格式是 custom 还是 plain?有没有遇到过 pg_dump "成功"但恢复时才发现对象缺失、物化视图为空、或者角色不存在的情况?另外,你的备份策略里,物理备份和逻辑备份各自承担什么角色?欢迎在评论区聊聊。
