20 深入专题:备份恢复与时间点恢复
基础备份、WAL 归档、PITR 与数据完整性校验。
DB吐槽大会为何说 PG 不支持 Partial PITR?
PG 原生 PITR 是整库级别的,无法只恢复库里的某张表或某个 schema 到过去某个时间点,而 Oracle 等支持表空间级/对象级恢复。要实现 Partial PITR 需要额外的表空间备份 + 手动组装,或借助第三方工具,运维复杂、成本高。
DB吐槽大会为何说 PG 无法 PITR 恢复到物理全量备份过程中的时刻?
PG 的在线备份是 pg_start_backup 到 pg_stop_backup 之间完成的,备份文件本身可能处于不一致的中间状态(备份过程中某 block 写一半被拷贝),必须回放到 stop 点之后的 WAL 才能保证一致。因此无法把 PITR 目标设到备份过程中某个时刻,只能恢复到 stop point 及之后的时间点。
DB吐槽大会为何说 PG 长期没有 block 级增量备份恢复?
此前 PG 原生只支持全量备份 + WAL 归档的 PITR,块级增量备份要靠 pg_rman、pg_probackup、ptrack、ZFS 快照等第三方方案。block 级增量备份可以只备份自上次备份以来变化的数据块,大幅节省空间和时间。直到 PG17 才内置了块级物理增量备份(WAL summarizer + pg_combinebackup)。
PG 全库一致性逻辑备份时,大表备份有哪些隐患?
全库一致性逻辑备份(pg_dump)需要一致性快照,大表备份耗时且需持有锁,可能引发锁等待和表膨胀。备份期间长期持有快照会阻止 vacuum 清理旧版本,导致表膨胀;大表 COPY 也可能长时间占用资源。建议大表用物理备份(pg_basebackup)或表级并行备份,配合合理的备份窗口。
PG 在线备份中,恢复的最小 WAL 范围(startpoint 到 stoppoint)是如何确定的?
pg_start_backup 后执行 checkpoint,backup_label 记录该 checkpoint 的 redo 位置(START WAL LOCATION),从这个位置开始有需要的 FPW(full page write)。备份文件中可能有 block 写一半被拷贝,必须回放到该 block 在 checkpoint 后第一次修改产生的 FPW,才能保证一致。stoppoint 是 pg_stop_backup 写入 WAL 的结束标记 LSN(standby 上则是 ControlFile->minRecoveryPoint)。t0~t3 之间的 WAL 是恢复的最小需求。
PG 崩溃恢复如何通过 prefetch 预读加速?
PG14/15 的 recovery 支持 prefetch 预读:提前把接下来要恢复的 WAL record 相关的 data block 读入 shared buffer,加速 WAL record 与 data block 的合并过程,减少恢复时的磁盘随机读等待。PG15 进一步支持异步 prefetch,覆盖崩溃恢复、逻辑/物理流复制、归档恢复等多种恢复场景。
PG 的 pg_checksums 在线校验和限速能力如何?
pg_checksums 1.0 起支持在线校验 block checksum(早期版本需停库),并支持限速(rate limit),兼容 9.3 以上所有 PG 版本。限速能力让校验大库时可以控制对磁盘 I/O 的冲击,避免影响在线业务。
PG12 的 pg_stat_database 新增 checksum 失败统计有什么意义?
PG12 在 pg_stat_database 新增 block checksum 失败计数列,统计属于该库的数据文件发生的 checksum 校验失败次数,包括正常 backend 处理时的失败和 base backup 检测到的失败。这能帮助尽早发现底层 I/O 系统的问题(坏块、静默数据损坏)。
PG14 支持 startup(恢复)进程与 backend 进程的死锁检测吗?
支持,PG14 引入了 startup 进程与 backend 进程之间的死锁检测(backpatch 到 9.6)。此前恢复进程与用户进程之间的死锁可能无法被检测,导致恢复被永久卡住。该特性在恢复冲突场景下检测恢复进程和用户查询之间的死锁并打破,避免 standby 恢复停滞。
PG14 的 DropRelFileNodeBuffers 优化解决了什么恢复性能问题?
SaaS 类用户在 drop 大量表/索引时,主库释放被删对象 shared buffer 用二分法查找很快,但从库每个对象删除都遍历整个 shared buffer,shared buffer 很大时一万个对象可能延迟数小时。PG14 在 recovery 路径优化 DropRelFileNodeBuffers:当待 truncate 的 block 数低于阈值时通过 BufMapping 表查找,避免全 buffer pool 扫描,性能提升百倍以上。
PG14 的 idle_session_timeout 和 idle_in_transaction_session_timeout 有何区别?
idle_in_transaction_session_timeout 只针对 idle in transaction(事务内空闲)的会话超时;PG14 新增的 idle_session_timeout 针对所有空闲会话(包括纯 idle)超时,自动断开空闲连接。两者都用于清理长期空闲会话、释放连接资源,避免连接风暴和资源占用。
PG14 的 log_recovery_conflict_waits 参数有什么用?
PG14 新增 log_recovery_conflict_waits,控制当 startup 进程因恢复冲突等待超过 deadlock_timeout 时是否打印日志。日志包含冲突对应的锁类型、导致冲突的 backend pid 等信息,方便观察冲突发生的历史事件,判断恢复冲突是否阻碍了 WAL 回放。
PG14 的 pg_locks 新增 wait_start 字段有什么用?
PG14 在 pg_locks 视图新增 wait_start 字段,记录锁等待开始的时间。这样 DBA 可以知道某个锁等待已经持续多久,便于结合 now() 判断等待时长,快速定位长时间未获得的锁,辅助分析锁争用和死锁风险。
PG14 的逻辑解码如何支持 2PC(两阶段事务)?
PG14 的逻辑解码新增对 2PC(两阶段事务、XA 事务)的支持。逻辑复制槽启用 two_phase 后,逻辑解码会输出 PREPARE TRANSACTION、COMMIT PREPARED、ROLLBACK PREPARED 事件,使订阅端能按两阶段边界处理分布式事务,保证跨资源提交的一致性语义可被复制。
PG15 为 archive_command/restore_command 等新增了什么等待事件?
PG15 新增了针对 archive_command、archive_cleanup_command、restore_command、recovery_end_command 等外部 shell 命令的等待事件。当这些命令执行缓慢时,可在 pg_stat_activity 的 wait_event 中看到对应等待,便于定位归档/恢复命令卡顿问题。
PG15 支持自定义归档 WAL 日志模块是什么意思?
PG15 允许通过模块(如 basic_archive 或第三方扩展)自定义归档 WAL 日志的行为,而不只是用 archive_command shell 命令。这提供了更灵活、可编程的归档方式,便于集成到对象存储、云归档等场景。
PG15 的 basebackup_to_shell 扩展插件是做什么的?
PG15 支持在插件中增加 pg_basebackup target,并提供内置的 basebackup_to_shell 扩展。配合 pg_basebackup,可以触发数据库 server 端执行自定义 shell 备份动作(如把备份直接写到挂载盘或远程),把备份逻辑下沉到服务端,扩展了 pg_basebackup 的备份目的地。
PG15 的 log_startup_progress_interval 参数有什么用?
PG15 新增 log_startup_progress_interval,支持打印 startup 进程长时间的恢复进度。当崩溃恢复/PITR 回放耗时较长时,按配置的时间间隔周期性输出恢复进度日志,让 DBA 知道恢复进行到哪一步、还差多少,避免恢复过程长时间无反馈。
PG15 的 pg_basebackup 在压缩方面有哪些增强?
PG15 的 pg_basebackup 增强压缩能力:支持客户端压缩和服务端(DB 端)压缩,压缩算法支持 gzip、lz4、zstd,并可指定压缩比选项(如 zstd 支持内置并行 workers)。服务端压缩可减轻网络传输量(代价是主库 CPU 开销),客户端压缩减轻磁盘占用。
PG16 的 pg_dump 批量加表级共享锁解决什么问题?
PG16 的 pg_dump 改为批量加表级共享访问锁,减少加锁交互次数和时长。此前逐个对象加锁交互多、耗时,改进后提升备份启动效率,也降低了备份期间与 DDL 交互的死锁窗口。
PG17 内置的块级增量备份(pg_combinebackup)是如何工作的?
PG17 新增 WAL summarizer 进程,自动生成 WAL summary 文件(存于 $PGDATA/pg_wal/summaries),记录一段 LSN 范围内每个 relation 被修改的 block 列表、文件创建/销毁、truncate 到的最小长度等信息。pg_basebackup –incremental=PATH_TO_MANIFEST 上传上一备份的 manifest 后生成增量备份(文件名为 INCREMENTAL.原名),pg_combinebackup 把全量+一个或多个增量合并为新的全量。
PG17 的 pg_restore –transaction-size=N 是什么?
PG17 的 pg_restore 新增 –transaction-size=N 选项,支持把 N 个对象封装为一个事务提交。这样在恢复大量对象时,可以控制每个事务包含的对象数量,平衡事务大小与失败重试粒度,避免一个超大事务恢复失败全部回滚,也避免每个对象单独提交导致恢复过慢。
PG19 的逻辑复制数据库级快照优化是什么?
PG19 对逻辑复制初始同步的数据库级快照做了优化(相关 patch 聚焦于 logical replication 的 snapshot 处理),改进快照生成与一致性维护方式,降低逻辑复制建链时对源库的影响,提升大库逻辑初始同步的效率。
PITR 恢复时,最后一个 WAL 文件没有事务结束 record 或 target name,能恢复到指定时间点吗?
能 promote。类似数据库 COPY 大量数据过程中 crash,最后一个有效 WAL 文件可能没有事务结束 record,但从检查点开始恢复、CLOG 恢复正常,仍能到一致性状态。能否区分是否恢复结束取决于 recovery_target_action:pause 时查 pg_is_wal_replay_paused() 是否返回 true,promote 时看是否已激活,shutdown 时看是否已停库。
PITR 恢复过程中如何手工得到下一个需要的 WAL 文件名?
默认由 restore_command 自动获取,若需手工得到,可用三种方法:一是让 restore_command 在找不到文件时把 %f 追加到日志文件(如 cp … || echo “%f” » /tmp/needwalfile);二是看数据库日志里 cp cannot stat 的输出;三是改内核用 UDF 获取(但受 hot_standby 限制)。恢复目标支持时间、还原点名字、事务 ID、WAL LSN 四种。
PostgreSQL 的增量备份和增量还原基本流程是什么?
基于 PG17 内置能力:先做一次全量备份(记录 manifest),之后用 pg_basebackup –incremental=PATH_TO_MANIFEST 生成增量备份(只含变化块),最后用 pg_combinebackup 把全量 + 一串增量合并还原为一个新的全量数据目录。WAL summarizer 后台进程负责统计每个 LSN 范围内变化的 block,summarize_wal 控制开关,wal_summary_keep_time 控制 summary 文件保留时长(默认 10 天)。
minimum recovery point(minRecoveryPoint)在恢复过程中如何推进?
minRecoveryPoint 是控制文件中的最小恢复点,表示数据库至少恢复到这个 LSN 才一致。恢复过程中它不断推进,记录已经安全应用到控制文件的重做位置。如果恢复中途 crash,必须重新到达这个点数据库才一致。在线备份在 standby 上执行时,备份结束位置就是 minRecoveryPoint。
pg_dump 并行备份为什么可能失败,报 ACCESS EXCLUSIVE lock 错误?
并行备份时 parent 进程先对所有对象加 shared lock,worker 备份每个表前再加 shared lock NOWAIT。若有客户端在此期间请求 ACCESS EXCLUSIVE 锁,该锁会排队并阻塞后续所有访问(包括 worker 的 shared lock NOWAIT),导致 worker 拿锁失败、整个 pg_dump 中止。解决:备份期间不要执行 DDL,或给用户设置 lock_timeout。
pg_filedump 是做什么用的?
pg_filedump 用于查看和诊断 PG 数据文件/数据页的底层内容,可 dump 出 page header、tuple header、行数据等二进制结构。当数据块损坏、需抢救数据或分析页结构时,用它直接读取堆文件内容,是数据块级恢复和排障的常用工具。
pg_verify_checksums / pg_checksums 工具是做什么的,有什么限制?
pg_verify_checksums 用于校验 PG 数据文件块的 checksum 一致性,需在停库(离线)状态下执行,识别到错误会直接退出。PG12 起重命名为 pg_checksums,并支持把数据文件从无 checksum 转为有 checksum(enable/disable)。数据文件块尾部记录 LSN,checksum 校验可验证恢复后的数据块是否逻辑一致。
pg_waldump 和 pg_verifybackup 分别用于检查什么?
pg_waldump 用于检查 WAL 文件、归档文件是否损坏,原理是校验 WAL record 的 CRC(WAL checksum 强制开启)。pg_verifybackup 检查 pg_basebackup 备份的数据文件是否有损坏,会调用 pg_waldump 解析备份中 WAL。注意 WAL 文件循环使用可能残留未初始化内容导致 pg_waldump 误报,属正常现象。
为什么基于表空间的在线备份和完全恢复是可行的?
恢复模式下 backup_label 文件的 START WAL LOCATION 优先级高于控制文件的 checkpoint 位置,PG 会从 backup_label 标记的 WAL 位置开始恢复。因此只需 backup_label + 表空间备份 + WAL 归档,就能把表空间恢复到最新状态。但如果要 PITR 恢复到过去某时刻(而非最新),还必须备份全局数据($PGDATA),否则全局数据块是未来的,元数据可能出错。
为什么开启归档但 archive_command 为空字符串会导致 WAL 堆积?
当 archive_mode 开启而 archive_command 是空字符串(默认)时,WAL archiving 被临时禁用,但服务器会继续累积 WAL 段文件并保留 archive_status 目录中的 .ready 状态文件,等待将来提供命令。若想开启归档但暂不真实拷贝,可配置一条返回 true 的命令(如 /bin/true)避免堆积,但会打断归档恢复所需的 WAL 链,应谨慎。
如何实现可选择性表空间(Selectivity Tablespace)备份和时间点恢复?
如果要只对部分表空间做备份并支持 PITR,必须同时备份全局数据($PGDATA)+ backup_label + 目标表空间 + WAL 归档。因为全局数据文件是最新的,若不能回放完整 WAL 到最新状态,全局数据里某些 block 可能是未来的,元数据内容会出错。只有时间线未分叉、redo 文件完整时理论上才安全。
开启 checksum 后遇到损坏 block 导致恢复失败,如何跳过?
可以临时设置 ignore_checksum_failure=on(仅 superuser 可改),使系统忽略 checksum 失败(仍报 warning)并继续处理,从而恢复出未损坏的元组。但该行为可能传播或隐藏损坏、导致 crash 等严重后果;若 block header 本身损坏仍会报错。默认 off,只建议在抢救数据时短暂开启。