PostgreSQL(Patroni)归档切换操作笔记

目录标题


📝 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) 行为总结(运行时流程)

  1. PostgreSQL 切段并调用 archive_command(替换 %p)。
  2. 首先执行阈值检查:若 .ready 数量 > 65536 → exit 0(跳过此文件)。
  3. 否则执行 pre_archive_push.sh %p:若成功,则执行 pgBackRest 的 archive-push(带 --archive-async,通常会马上返回成功并把文件放到 spool,实际上传由 archive-push:async 后台进程执行)。
  4. PostgreSQL 接收到命令的退出码来判断是否归档成功(0 = 成功)。你用了 exit 0 跳过是避免报错。

5) 风险与注意点(必须知道)

  1. ls | grep | wc -l 的 race 和 edge-case

    • 若目录非常大或包含特殊字符(例如空格、新行、控制字符),ls/grep 处理可能有问题。ls 不安全用于脚本计数。
    • 在高并发情况下(多个 archive_command 同时运行),在 lswc 之间可能出现竞态(count 可能短暂不准确)。虽然跳过行为是宽容的(只是判断是否 > 阈值),但仍建议用更稳健方法(见改进)。
  2. exit 0 的语义

    • 你选择 exit 0 来跳过(返回成功),这样 PostgreSQL 不会把它当作归档失败。但这意味着这段 WAL 不会进入 repo(潜在数据恢复缺口)。请确认跳过策略与备份/恢复 SLA 匹配。
  3. 路径/权限

    • pre_archive_push.shpgbackrest 的路径必须为绝对路径且 postgres 用户可执行。
    • Patroni 下发后,Postgres 会用 postgres 用户执行该命令。
  4. --force 的副作用

    • patronictl --force 会覆盖配置,可能覆盖其他管理员的并发修改,使用时要小心。
  5. 监控 spool 空间

    • 异步模式会产生 spool 文件;需要监控 spool/磁盘,避免磁盘被写满导致 DB 因无法生成 WAL 而阻塞。
  6. 日志与调试

    • 你把日志等级设为 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) 推荐的运维步骤(在落地前)

  1. 测试环境先验证新命令(尤其含 findflock 的版本)。
  2. 在主库执行 SELECT pg_switch_wal(); 多次,观察 archive_status、spool、pgBackRest 日志(/tmp/db-archive-push.log...-async.log)。
  3. 确认 pre_archive_push.sh 的 exit 码与行为(在脚本中做好日志、失败重试或告警)。
  4. 配置监控:spool 文件数、spool 磁盘使用、pgBackRest 上传成功率。
  5. 如果使用 --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

意思是:

  1. 数一下 pg_wal/archive_status/*.ready 的数量
  2. 如果超过 65536 个(约 1TB WAL 量)
  3. 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 在本地爆盘?还是担心归档库太大?

我可以给你一个最符合业务的版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值