
凌晨四点十七分告警群突然刷屏。定时任务里pgbackrest backup的日志停在一条 ERROR 上incremental backups cannot be taken unless WAL summarization is enabled。数据库实例本身一切正常CPU、连接数、慢查询都没有波动但备份确实失败了。做 DBA 的人都懂这种报错最麻烦——它不是数据库挂了而是备份链路里某个环节没有满足前置条件而且是那种看起来什么都没坏就是跑不起来的前置条件。如果你遇到的是同样的问题说明你在用 pgBackRest 做 PostgreSQL 的增量备份--typeincr而当前备份仓库没有开启或者没有生成 WAL 摘要信息WAL summarization。这篇文章不打算只给你一句加上summarize-waly我会把这个报错的来龙去脉、排查链路、以及我踩过的几个隐形坑一次性讲清楚帮你下次遇到同类问题能自己定位而不是靠搜报错碰运气。1. 报错现场一条被调度系统刷屏的 ERROR1.1 你会在什么场景下看到它这条报错绝大多数情况下出现在两种操作里第一种是你手动执行pgbackrest backup --stanzamain --typeincr终端直接吐错第二种是定时任务里的增量备份失败然后告警系统把 stderr 里的内容原样发到群里。我遇到的具体场景是团队接手了一套已经稳定跑了两三个月的 pgBackRest 环境之前一直用--typefull每周全备某天为了缩短备份窗口把调度改成了周日全备 周一到周六增量结果改造后的第一次增量备份就报了这个错。$ pgbackrest backup --stanzamain --typeincr ERROR: [047]: incremental backups cannot be taken unless WAL summarization is enabled注意这里的[047]是 pgBackRest 的进程号不是错误码。真正需要关注的是冒号后面的内容。同时 pgBackRest 的日志文件默认在 PostgreSQL 数据目录的pgbackrest日志目录下里会记录更完整的堆栈上下文包括它检测到备份仓库中缺少哪些 WAL 摘要文件。1.2 这确实是 pgBackRest 的专属报错很多 DBA 看到 WAL 就以为是 PostgreSQL 原生报错跑去翻pg_log结果什么都找不到。其实原生pg_basebackup和pg_probackup都不会给出这条消息。它是 pgBackRest 在备份驱动层主动抛出的一个安全检查错误意思是你要求的增量备份在当前仓库配置下无法保证一致性所以拒绝执行。搞清楚这一点非常重要因为排查方向从一开始就不应该是数据库怎么了而应该是pgBackRest 的 WAL 摘要链路怎么了。顺便说一句如果你用的是 barman 或者只是普通的archive_command拷贝 WAL那根本不会出现这个报错因为它没有增量备份的WAL 摘要概念。看到这条 ERROR基本可以直接锁定 pgBackRest 为排查对象。2. WAL summarization 究竟是什么把日志变成索引卡片2.1 从 WAL 说起为什么备份必须依赖日志PostgreSQL 的 WALWrite-Ahead Log机制是这一切的起点。任何数据页的修改都会先写入 WAL 日志再落盘到真正的数据文件。所以数据文件在任意时刻都可能是不完整的真正完整的状态 某个时间点的数据文件快照 该时间点之后的所有 WAL 日志。备份的本质也是捕获这份快照和日志的组合。pgBackRest 做全量备份时会记录两个关键 LSNbackup start开始备份那一刻和backup stop备份结束、数据库一致性点。在全量备份完成之后只要从backup start对应的 WAL 位置开始回放数据就能恢复到一致状态。这个机制可以打一个比方WAL 是黑匣子数据文件是机舱。你想还原一次航班发生了什么光有落地后的机舱状态不够还得有起飞后的黑匣子记录。备份工具要做的就是把这两个东西的对应区间精确对上。2.2 WAL 摘要文件的构成和生成时机WAL summarization 是 pgBackRest 2.23 版本引入的能力。开启它之后pgBackRest 在归档每个 WAL 段时会额外生成一份 JSON 格式的摘要文件。这份摘要不是 WAL 本身而是对一段 WAL 的索引卡片包含这一批 WAL 段的文件名、字节大小、起始 LSN、结束 LSN以及这个范围内是否出现空洞等信息。我的理解是它把这个仓库里有哪些 WAL、覆盖了什么 LSN 区间这件事变成了一个可以快速查询的目录而不是每次都要去 Archive 目录里把文件列表遍历一遍。摘要文件存放在备份仓库中该 stanza 的wal-summary路径下和备份元数据放在一起。生成时机并不神秘每当archive_command成功调用pgbackrest archive-push推送一个新 WAL 段如果配置了summarize-walypgBackRest 就会尝试更新或追加对应的摘要文件。也就是说没有 archive 的 WAL 推送就没有摘要。这是后面排查时一个很容易被忽略的关联点。3. 增量备份依赖它的三个底层原因3.1 增量备份的增量到底是什么很多人以为增量备份就是拷贝日志文件这其实是误解。pgBackRest 的增量备份以及 differential 差异备份仍然是拷贝数据文件只不过只拷贝从上一个备份基准以来发生过变化的那些数据页。备份类型拷贝的数据范围恢复时需要回放的 WAL 区间full全库所有数据文件本次备份的 start LSN 到 stop LSNdifferential自上次 full 以来变化的页面上次 full 的 stop LSN 到本次 stop LSNincremental自上次任意类型备份以来变化的页面上次备份的 stop LSN 到本次 stop LSN增量备份之所以省时间是因为它不需要扫描全库而是在文件系统或数据库页面上做差异判断。但省下来的时间是有代价的恢复时它必须精确知道从上一个基准备份的一致性点到本次备份一致性点之间的 WAL 都在哪里、是否完整。3.2 一致性边界需要 WAL 的精确区间假设你的备份链是周日 fullstop LSN A→ 周一 incrstop LSN B→ 周二 incrstop LSN C。恢复周二的增量备份时pgBackRest 需要从 A 开始连续回放覆盖 [A, B] 和 [B, C] 的所有 WAL。如果有任何一个 WAL 段缺失、大小不对、或者 LSN 区间不连续恢复过程可能在中间某个点崩掉而且往往是几小时之后才崩。WAL 摘要文件提供的就是这个完整区间的快速判定能力。pgBackRest 拿到周二的增量备份后会去查摘要确认 A 到 C 之间是否所有 WAL 段都在仓库里、有没有空洞。如果摘要显示缺了一段备份工具会立刻报错或做特殊处理而不是等到恢复时才暴露问题。没有摘要PGBackRest 也不是完全不能工作但那就只能靠遍历 archive 目录里的文件名来猜。文件一多、段大小一改、或者归档中途有过断档这种猜就容易出错。增量备份是建立在多个备份之间相互依赖的基础上的任何一个环节模糊都会影响整条备份链的可恢复性。3.3 没有摘要时的模糊地带与数据风险我在测试环境做过一个实验把summarize-wal关掉手动删掉仓库里一个历史 WAL 段然后跑增量备份。pgBackRest 最终给出的就是这个cannot be taken unless...的拒绝而不是继续备份。刚开始我觉得它不近人情——我明明是做增量你为什么非要管 WAL 摘要后来想明白了这正是它负责任的表现。因为如果它放你备份备份本身可能成功但等到恢复演练时你会发现那个被删掉的 WAL 段永远补不回来恢复只能停在某个时间点之前。与其产出一个注定不可用的备份不如在一开始就拒绝你。所以请把这个 ERROR 看作是 pgBackRest 的安全门禁而不是一个可以绕过去的小障碍。4. 从 ERROR 到恢复完整排查与修复链路4.1 第一步确认版本和配置状态排查的首要任务是弄清楚你的 pgBackRest 版本是多少summarize-wal到底有没有开开了之后摘要到底有没有在生成。pgbackrest version pgbackrest check --stanzamain pgbackrest info --stanzamain然后直接看配置文件grep -nE summarize-wal|archive-cmd|archive-mode|repo1-path /etc/pgbackrest/pgbackrest.conf一个常见的迷惑点是summarize-wal这个配置项虽然存在但很多旧配置文件里根本没有它。因为 pgBackRest 官方文档规定增量备份本身会要求summarize-waly理论上它会自动开启但如果配置里显式把它设成了n或者某个 stanza 级别的配置覆盖了全局配置增量备份就会被拦住。这里我补充一句基于常见实践的说明如果你是从 2.23 之前的旧版本升级上来的配置文件里可能没有这个选项的痕迹导致你需要手动补充。具体是全局开启还是只在某个 stanza 开启取决于你有没有多个 stanza但通常我建议全局开启。4.2 第二步开启 summarize-wal最简单的方式是直接编辑配置[global] repo1-path/var/lib/pgbackrest summarize-waly [stanza:main] pg1-path/var/lib/postgresql/14/main如果你只是想临时试一次不想改配置文件也可以走命令行pgbackrest backup --stanzamain --typeincr --summarize-waly注意命令行参数只在这一次备份中生效。我建议你最终还是要写进配置文件否则下一次手动执行增量备份又会踩到同样的坑。有一个细节容易忽略如果 pgBackRest 是通过后台 daemon 方式运行的pgbackrest --stanzamain start改完配置后需要重启 daemon如果是靠archive_command每次调用命令行客户端那下一次执行时自然就会读到新的配置不需要重启任何东西。4.3 第三步验证摘要文件真的在生成很多人在这里改完配置重跑增量备份发现仍然是同样的 ERROR就开始怀疑文档写错了。其实这不是配置没生效而是摘要文件需要有新的 WAL 段归档才会生成。如果上次归档已经是很久之前的事仓库里自然还没有对应的摘要pgBackRest 也就无从确认 LSN 区间覆盖。你可以这样验证find /var/lib/pgbackrest/backup/main -path *wal-summary* -type f | tail -20如果这个目录里一个文件都没有说明 WAL 摘要链路完全没有工作。这时不要急着纠结增量备份先去检查archive_mode和archive_commandpsql -c SHOW archive_mode; psql -c SHOW archive_command; SELECT * FROM pg_stat_archiver;pg_stat_archiver里如果failed_count在增长说明 WAL 推到 pgBackRest 仓库的路径本来就不通。增量备份报错只是一个下游症状上游的归档链路才是需要先修的地方。4.4 第四步重跑增量备份并验证一致性在摘要文件正常生成之后重新执行pgbackrest backup --stanzamain --typeincr --summarize-waly如果还是报错我的建议是再看一眼仓库里有没有可用的 full 备份作为基准。增量备份必须依赖一个此前完成的 full 备份如果这个 stanza 从来没有成功生成过 full 备份或者 full 备份的元数据损坏pgBackRest 也会给出类似的拒绝。这种场景下先跑一次全量备份建立新的基线pgbackrest backup --stanzamain --typefull全量完成后再跑--typeincr通常就顺了。备份成功后用pgbackrest info --stanzamain确认状态stanza: main status: ok cipher: none db (current) wal archive min/max (14): 000000010000000000000001 / 0000000100000000000000A0 full backup: 20250601-040000F timestamp start/stop: 2025-06-01 04:00:00 / 2025-06-01 04:12:30 start lsn/stop lsn: 0/2000028 / 0/9000A28 incr backup: 20250602-040000I timestamp start/stop: 2025-06-02 04:00:00 / 2025-06-02 04:07:18 start lsn/stop lsn: 0/9000C28 / 0/E000A28一致性验证这件事我不建议只看到备份成功就结束。趁数据库负载低的时候在测试实例上做一次真实恢复演练确认备份能恢复、数据能起来。遇到这种报错之后最重要的不是把报错消掉而是确认备份链路重新变得可信。5. 排查之外增量备份策略里常见的三个隐形雷区5.1 archive 链路没打通摘要文件无从生成第一个雷区就是我在 4.3 里提到的你只开了summarize-waly但archive_command写得有问题。比如有人图省事写成了cp %p /backup/wal/%f直接把 WAL 拷贝到本地目录而 pgBackRest 仓库根本收不到任何 WAL摘要也就永远不会生成。这个时候增量备份报错只是其中一个信号更早的信号其实是pg_stat_archiver里不断增长的失败次数。检查archive_command时不仅要看它的内容还要确认 pgBackRest 配置的archive-push指定的repo1-path与实际写入路径一致。我曾经遇到过路径不一致的情况归档命令指向/var/lib/pgbackrest但配置文件里写的是/tmp/pgbackrest结果所有 WAL 都进了临时目录备份仓库里空空如也。这种问题从日志上很难一眼看出来配置文件对照着看就非常明显。5.2 备库场景下 standby 配置的坑如果你在用backup-standby从备库执行备份WAL 摘要的生成方式会有微妙差异。备库本身的archive_mode通常是on但很多部署团队只在主库上配置了archive_command指向 pgBackRest备库的archive_command是空的。这样备库就算参与备份也无法提供完整的 WAL 归档摘要文件自然缺位。合理做法基于我自己的部署经验是主备库都用 pgBackRest 的archive-push作为归档命令并且在 pgBackRest 配置里为两个节点都设置好对应的pg1-path和连接参数。否则从备库启动增量备份时pgBackRest 拿不到备库这一侧完整的 WAL 范围严重时也会触发这条 ERROR或者备份成功后恢复时发现日志区间对不上。5.3 版本混用主库、备份机、pgBackRest 版本不一致另一个容易被忽略的雷区是版本一致性。WAL 摘要文件的格式在不同 pgBackRest 版本之间并不是完全稳定的尤其是我印象中 2.23 之后几个小版本都对摘要内容有迭代。如果你主库上跑的是 2.28备份仓库所在的机器上却还是 2.24archive-push 生成的摘要可能在新版本解析时遇到兼容性问题轻则报奇怪错误重则增量备份初始化失败。我的习惯是同一套 stanza 涉及的所有节点主库、备库、仓库主机pgBackRest 版本保持完全一致。升级时先规划停机窗口逐个节点升级并验证不要图省事只升仓库机或者只升数据库机。5.4 一个快速止损的应急方案如果你的业务不允许浪费时间去排查备份任务又必须按时完成最保守的止损手段是先跑一次全量备份保证今天确实有一份可用备份pgbackrest backup --stanzamain --typefull全量备份不需要 WAL 摘要信息因此这条 ERROR 不会挡住它。等全量备份落地、确认仓库健康后再按 4.1 到 4.4 的链路去排查增量备份的事。千万别在增量备份持续失败的情况下连续重试三天——那只会积累备份一直失败的风险而不会让问题自动变好。我在生产环境里的习惯是即使暂时不需要增量备份也把summarize-wal保持开启。因为一旦以后需要增量备份或者做细粒度的恢复演练这个功能就是基础设施而不是临时要开的开关。平时开启它没有任何副作用最多是多生成一些体积很小的 JSON 摘要文件换来的是备份链路的可验证性。这个取舍我认为非常值得。