目录标题
- 📝 PostgreSQL(Patroni)归档切换操作笔记
- 1. 查看当前归档配置(不影响业务)
- 2. 在线切换归档命令(不会影响业务)
- 3. 切换到其他归档模式(可选)
- 4. 切换后验证是否成功
- 5. 异步归档日志含义(理解重点)
- 6. 常用排查命令(可直接复制)
- 7. 回滚示例(如归档切换后要恢复原配置)
- 8. 注意事项(实际运维必须关注)
- 命令(原样)
- 1) 外层 — `patronictl` 部分
- 2) 被写入的 `archive_command`(逐部分)
- 3) `%p` 的含义
- 4) 行为总结(运行时流程)
- 5) 风险与注意点(必须知道)
- 6) 改进建议(更稳健、安全)
- 7) 推荐的运维步骤(在落地前)
- 8) 小结(一句话)
- ✅ 你现在配置的行为(关键在 archive_command 里的 ready 文件计数)
- ❌ **不会覆盖旧归档文件**
- ✅ **真正发生的是:超过 1TB 后,新 WAL 不再上传**
- ⚠️ 这个逻辑的风险是什么?
- 如果你的目标是:
- ✔️ 方案 A:让 pgBackRest 自动控制 WAL 保留量
- ✔️ 方案 B(安全版本):只在 spool 内做限流,不跳过归档
- 📌 最后总结(非常重要)
📝 PostgreSQL(Patroni)归档切换操作笔记
(pgBackRest 异步归档版,日志级别已调整为 info)
本笔记适用于使用 Patroni 管理的 PostgreSQL 主库,归档方式为 pgBackRest 异步归档,并包含如何切换、验证、排查与回滚。
1. 查看当前归档配置(不影响业务)
psql -c "SHOW archive_mode;"
psql -c 'SHOW archive_command;'
psql -c "SHOW archive_timeout;"
查看 Patroni 下发的当前配置:
patronictl show-config
2. 在线切换归档命令(不会影响业务)
修改后 PostgreSQL 会自动 reload,不需要重启,也不会导致切主。
下面是推荐的归档命令(异步 + 日志级别为 info):
★ 推荐:使用 pgBackRest 异步归档(性能最好)
归档命令(已设定阈值 65536 ≈ 1TB 的 WAL)
当
archive_status里的.ready超过阈值时跳过归档,避免积压扩散。
patronictl edit-config --force --quiet \
-p archive_command='if [[ $(ls $(dirname %p)/archive_status | grep \.ready$ | wc -l) -gt 65536 ]]; then exit 0; fi; /home/postgres/bin/pre_archive_push.sh %p && pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p'
📌 特点
- 不阻塞 PostgreSQL
- WAL 由异步后台进程批量并发上传
- 日志简化为 info 级别
3. 切换到其他归档模式(可选)
3.1 切换到 本地目录归档(简单模式)
用于排错或远端仓库不可达时的临时方案:
patronictl edit-config --force --quiet \
-p archive_command='mkdir -p /var/lib/pg_wal_archive && cp %p /var/lib/pg_wal_archive/ && chmod 600 /var/lib/pg_wal_archive/$(basename %p)'
3.2 临时关闭归档(慎用)
patronictl edit-config --force --quiet -p archive_mode=off
⚠️ 风险:关闭归档会导致不可做 PITR & 影响增量备份链。仅用于紧急问题处理。
4. 切换后验证是否成功
4.1 强制切 WAL 段
SELECT pg_switch_wal();
4.2 确认 archive_status 目录是否生成新 .ready 文件
ls -ltr /pgdata/data/postgres-74334556/pg_wal/archive_status | tail -20
4.3 查看 pgBackRest 归档日志
异步批处理日志:
tail -f /tmp/db-archive-push-async.log
单条 archive_command 触发日志:
tail -f /tmp/db-archive-push.log
5. 异步归档日志含义(理解重点)
archive-push.log
每次 PostgreSQL 触发 archive_command → pgBackRest 收到请求(单文件级别)
例:
pushed WAL file '000000010000000000000004' to the archive asynchronously
archive-push-async.log
异步后台进程扫描 spool → 批量上传 WAL(真正上传动作)
例:
push 2 WAL file(s) to archive:
000000010000000000000004.00000028.backup ... 000000010000000000000004
📌 区别总结
- archive-push.log = PostgreSQL 发起
- archive-push-async.log = pgBackRest 异步后台处理
- 最终实际上传行为在 async 日志中
6. 常用排查命令(可直接复制)
查看 spool 积压数量:
ls -1 /pgdata/data/postgres-74334556-spool | wc -l
查看 pgBackRest 状态:
pgbackrest --stanza=db info
检查当前配置一致性:
patronictl show-config
psql -c "SHOW archive_command;"
7. 回滚示例(如归档切换后要恢复原配置)
恢复为非异步(只同步 archive-push):
patronictl edit-config --force --quiet \
-p archive_command='/home/postgres/bin/pre_archive_push.sh %p && pgbackrest --log-level-console=info --log-level-file=info archive-push %p'
8. 注意事项(实际运维必须关注)
- 异步归档模式下需注意 spool 目录空间,防止积压导致 WAL 爆盘。
- archive_command 的脚本路径必须绝对路径,并确保 postgres 用户可执行。
- Patroni edit-config 只会触发 reload,不会导致数据库重启。
命令(原样)
patronictl edit-config --force --quiet -p archive_command='if [[ $(ls $(dirname %p)/archive_status | grep \.ready$ | wc -l) -gt 65536 ]]; then exit 0; fi; /home/postgres/bin/pre_archive_push.sh %p && pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p'
下面把整条命令逐段拆解并详细解释,并给出常见风险、改进建议与一个更稳健的替代命令供直接使用。
1) 外层 — patronictl 部分
patronictl edit-config
使用 Patroni 的 CLI 修改集群配置(即写入 DCS 并下发到 PostgreSQL 节点)。--force
强制写入,即使检测到潜在冲突也强制应用(请谨慎使用)。--quiet
静默模式,减少输出(适合脚本/自动化)。-p archive_command='... '
指定要写入postgresql.parameters.archive_command的值(单行字符串)。Patroni 会把这个参数下发并触发 PostgreSQL reload,使新archive_command在线生效(不会重启数据库)。
2) 被写入的 archive_command(逐部分)
archive_command 的主体是一个 shell 命令串,Postgres 在每次 WAL segment 生成并需要归档时会把 %p(WAL 文件全路径)替换进去并执行该字符串作为 shell 命令。
命令分为两段,用 ; 分隔(短路逻辑 + 执行归档):
A. 阈值检查并可能跳过归档
if [[ $(ls $(dirname %p)/archive_status | grep \.ready$ | wc -l) -gt 65536 ]]; then exit 0; fi;
-
$(dirname %p):计算 WAL 文件路径%p的目录(%p由 PostgreSQL 在运行时替换为 WAL 文件的完整路径)。 -
$(dirname %p)/archive_status:Postgres 的archive_status目录(用于标记哪些 WAL 段已准备好被归档,通常包含.ready/.done 等文件)。 -
ls $(dirname %p)/archive_status:列出该目录下的内容(注意:如果目录为空或有特殊文件名,ls可能有副作用;见下文风险)。 -
grep \.ready$:筛出以.ready结尾的文件名(那些表示“待归档”标记)。 -
wc -l:统计.ready文件行数(即待归档文件数)。 -
-gt 65536:如果数量 > 65536(你设定的阈值,约等于 1TB / 16MB),则then exit 0;:脚本以exit 0返回成功(表示跳过当前归档,不报错给 PostgreSQL)。
-
fi;:结束if。
目的:当待归档文件过多时(积压达到阈值),短路跳过当前推送,避免继续往 spool/远端推送导致更大压力。
B. 真实归档流程(执行脚本并调用 pgBackRest)
/home/postgres/bin/pre_archive_push.sh %p && pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p
-
/home/postgres/bin/pre_archive_push.sh %p:先执行你自定义的预处理脚本(例如权限设置、校验、移动到 spool 等)。若该脚本返回非 0,则后续命令不会执行(&&逻辑)。 -
pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p
调用 pgBackRest 的archive-push,参数说明:--log-level-console=info:控制台日志级别为 info。--log-level-file=info:文件日志级别为 info(你要求的修改)。--archive-async=y:启用异步(spool)归档模式 —— pgBackRest 接收请求后会把文件放入 spool,由后台异步进程批量发送,不阻塞 PostgreSQL。--process-max=8:异步上传时的并发子进程数(最多 8 个并行上传)。archive-push %p:把%p指定的 WAL segment 归档到 repo(实际行为会因为--archive-async变成写入 spool,由 async 进程上传)。
3) %p 的含义
%p:Postgres 在执行archive_command时做替换,替换为要归档的 WAL 文件的完整路径(例如/pgdata/data/.../pg_wal/000000010000000000000004)。archive_command字符串必须支持这个替换位置。
4) 行为总结(运行时流程)
- PostgreSQL 切段并调用
archive_command(替换%p)。 - 首先执行阈值检查:若
.ready数量 > 65536 →exit 0(跳过此文件)。 - 否则执行
pre_archive_push.sh %p:若成功,则执行 pgBackRest 的archive-push(带--archive-async,通常会马上返回成功并把文件放到 spool,实际上传由archive-push:async后台进程执行)。 - PostgreSQL 接收到命令的退出码来判断是否归档成功(0 = 成功)。你用了
exit 0跳过是避免报错。
5) 风险与注意点(必须知道)
-
ls | grep | wc -l的 race 和 edge-case- 若目录非常大或包含特殊字符(例如空格、新行、控制字符),
ls/grep处理可能有问题。ls不安全用于脚本计数。 - 在高并发情况下(多个
archive_command同时运行),在ls与wc之间可能出现竞态(count 可能短暂不准确)。虽然跳过行为是宽容的(只是判断是否 > 阈值),但仍建议用更稳健方法(见改进)。
- 若目录非常大或包含特殊字符(例如空格、新行、控制字符),
-
exit 0的语义- 你选择
exit 0来跳过(返回成功),这样 PostgreSQL 不会把它当作归档失败。但这意味着这段 WAL 不会进入 repo(潜在数据恢复缺口)。请确认跳过策略与备份/恢复 SLA 匹配。
- 你选择
-
路径/权限
pre_archive_push.sh和pgbackrest的路径必须为绝对路径且 postgres 用户可执行。- Patroni 下发后,Postgres 会用 postgres 用户执行该命令。
-
--force的副作用patronictl --force会覆盖配置,可能覆盖其他管理员的并发修改,使用时要小心。
-
监控 spool 空间
- 异步模式会产生 spool 文件;需要监控 spool/磁盘,避免磁盘被写满导致 DB 因无法生成 WAL 而阻塞。
-
日志与调试
- 你把日志等级设为
info(console/file),可以看到常规信息,但如果排查失败,可能还需detail用于更深入诊断。
- 你把日志等级设为
6) 改进建议(更稳健、安全)
下面给出几条可行改进(按优先级)以及直接可替换的更稳健命令。
建议
- 用
find或 glob-safe 的计数替代ls|grep|wc(更安全,对特殊文件名更耐受)。 - 在计数/判定上,加上
|| true以避免命令失败导致archive_command失败。 - 对阈值检查使用锁(
flock)以避免并发竞态(可选)。 - 明确设置
PATH或使用绝对路径(推荐)。 pre_archive_push.sh内部也要做重试/幂等处理。
更稳健的替代(无锁版)
patronictl edit-config --force --quiet -p archive_command='count=$(find $(dirname %p)/archive_status -maxdepth 1 -type f -name "*.ready" 2>/dev/null | wc -l); if [ "${count:-0}" -gt 65536 ]; then exit 0; fi; /home/postgres/bin/pre_archive_push.sh %p && /usr/bin/pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p'
说明:
find ... -name "*.ready"比ls | grep更安全(能正确处理空目录、特殊文件名)。2>/dev/null屏蔽找不到目录时的错误输出,避免影响计数。- 用
count变量提高可读性。
更稳健的替代(带锁,避免并发竞态)
假设系统有 flock,放一个简单的文件锁 /var/lock/pg_archive.lock:
patronictl edit-config --force --quiet -p archive_command='flock -n /var/lock/pg_archive.lock bash -c "count=$(find $(dirname %p)/archive_status -maxdepth 1 -type f -name \"*.ready\" 2>/dev/null | wc -l); if [ \"${count:-0}\" -gt 65536 ]; then exit 0; fi; /home/postgres/bin/pre_archive_push.sh %p && /usr/bin/pgbackrest --log-level-console=info --log-level-file=info --archive-async=y --process-max=8 archive-push %p" || exit 0'
说明:
flock -n非阻塞尝试获得锁,若拿不到则直接exit 0(跳过),避免多个并发archive_command互相等待或重复计数。- 注意:需要确保
/var/lock/pg_archive.lock对 postgres 用户可写(或使用 root 管理脚本来写锁并设置权限),并且系统有flock(大多数 Linux 都有)。
7) 推荐的运维步骤(在落地前)
- 在测试环境先验证新命令(尤其含
find或flock的版本)。 - 在主库执行
SELECT pg_switch_wal();多次,观察archive_status、spool、pgBackRest 日志(/tmp/db-archive-push.log和...-async.log)。 - 确认
pre_archive_push.sh的 exit 码与行为(在脚本中做好日志、失败重试或告警)。 - 配置监控:spool 文件数、spool 磁盘使用、pgBackRest 上传成功率。
- 如果使用
--force,变更前在团队里做一次变更通知。
8) 小结(一句话)
原命令会把阈值检查 + 预处理脚本 + pgBackRest 异步归档串成一个在线生效的 archive_command,可以满足“当归档积压超阈值时跳过归档并用 pgBackRest 异步上传”的需求;不过建议用 find 替代 ls|grep 并考虑用 flock 或其它并发控制、并确保脚本和锁文件对 postgres 用户可用,以提升健壮性与安全性。
结论先说:
不是覆盖,而是“超过限制后,不再继续归档”,旧的数据不会被覆盖,只是新的 WAL 不会被推送到归档仓库。
下面是你当前逻辑的真实行为解释。
✅ 你现在配置的行为(关键在 archive_command 里的 ready 文件计数)
你的 archive_command 里有这一段:
if [[ $(ls $(dirname %p)/archive_status | grep \.ready$ | wc -l) -gt 65536 ]]; then
exit 0
fi
意思是:
- 数一下
pg_wal/archive_status/*.ready的数量 - 如果超过 65536 个(约 1TB WAL 量)
archive_command直接 exit 0(表示成功),不执行 pgbackrest 归档
因此:
❌ 不会覆盖旧归档文件
pgBackRest 从不自动覆盖仓库里的旧 WAL ———— 因为 WAL 名字是递增的。
例如:
000000010000000000000001
000000010000000000000002
...
这是一个单调递增序列,不会覆盖前面的文件。
你即使再次 archive-push,也不允许覆盖(pgBackRest 会拒绝)。
所以 绝对不会出现覆盖“前 1TB 数据”的情况。
✅ 真正发生的是:超过 1TB 后,新 WAL 不再上传
你现在的逻辑是:
- 达到阈值 → 新 WAL 推送被跳过
- 但 PostgreSQL 本地 pg_wal 目录仍然会继续生成新的 WAL 文件
结果:
-
归档仓库(backrest repo)冻结在 1TB,不再增加
-
pg_wal 本地仍不断变大
- PostgreSQL 仍要保留所有未归档的 WAL(因为它认为归档“成功”,但其实你没有真正推走)
⚠️ 这个逻辑的风险是什么?
因为 exit 0 代表“归档成功”,PostgreSQL 会认为:
这些 WAL 已经被安全归档,可以删除。
但实际上:
你根本没有把 WAL 上传到 backrest repo!
这样会造成:
❗ 备份恢复不可用(缺 WAL)
全备 + 增量恢复时会发现 WAL 不足。
❗ standby(流复制备库)不受影响,但 archive 模式会失效
❗ 本地 pg_wal 不会无限增长,但备份将不可恢复到历史点
如果你的目标是:
🎯 “只保留最近 1TB 的 WAL,旧的自动清理,新 WAL 继续归档”
那么正确做法应该是:
✔️ 方案 A:让 pgBackRest 自动控制 WAL 保留量
调整:
repo1-retention-archive-type=time
repo1-retention-archive=1
repo1-retention-full=...
例如:
repo1-retention-archive=1
repo1-retention-archive-type=size
repo1-retention-archive=1000GB
(pgBackRest 2.50+ 已支持 retention-archive=size)
这样:
- 超过 1TB → pgBackRest 自动删除旧 WAL
- 新 WAL 继续归档
- PostgreSQL 恢复链路不会断
- 不需要自己写 dangerous shell 判断
✔️ 方案 B(安全版本):只在 spool 内做限流,不跳过归档
也可以调整 async queue 逻辑,但要比你现在的安全得多。
如果你想要,我可以帮你生成一套 完全安全、不会丢 WAL、不会影响恢复能力 的配置。
📌 最后总结(非常重要)
你当前“1TB 阈值”逻辑 ≠ 保留最近 1TB
而是:
达到 1TB 后,新 WAL 不再归档,导致归档链断裂,备份不可恢复。
如果你的目标是“保留最近的 1TB WAL”,那这不是正确方式。
如果你告诉我你的实际需求:
- 是否做 PITR?
- 是否需要备库从 archive 恢复?
- 是否想限制 repo 大小?
- 是担心 pg_wal 在本地爆盘?还是担心归档库太大?
我可以给你一个最符合业务的版本。
1947

被折叠的 条评论
为什么被折叠?



