Skip to content

WAL 积压监控 ​

WAL(Write-Ahead Log)是 PostgreSQL 的心脏:所有变更先写 WAL 再落数据页,主备复制靠传 WAL、PITR 靠归档 WAL、崩溃恢复靠重放 WAL。而「WAL 积压」指的是:WAL 已经产生,但它的下游消费者(备库、归档进程、复制槽消费方)没有及时跟上,于是这些 WAL 文件必须被保留在 pg_wal 目录里等待消费。下游停得越久,积压越多,磁盘被吃掉越快——WAL 积压是 PostgreSQL 运维中最容易演变成「磁盘写满、整库崩溃」的事故路径,因为它通常是静默的:下游断连不会报任何错误给主库,主库只是默默地把 WAL 留下来。

三类典型场景会造成积压:复制槽失活(下游实例删除/迁移后忘了删槽,槽永远等不到消费)、归档失败(archive_command 目标存储不可达,归档停在某个文件)、备库断连(备库宕机、网络分区、回放挂起)。三者的表象都是 pg_wal 目录持续膨胀,根因与处置却完全不同,因此本页的核心是教你用一组系统视图把「是哪类积压、积压多少、还能撑多久」三个问题答清楚。

本页将依次覆盖:pg_wal 目录大小的监控方法、基于 LSN 的积压量计算、pg_replication_slots 中 restart_lsn / wal_status / safe_wal_size 的解读、max_slot_wal_keep_size 防护机制、pg_stat_archiver 归档健康分析、副本延迟的度量 SQL、告警阈值设计,以及积压的应急处理流程(含 drop 槽前确认与 pg_replication_slot_advance 的用途风险)。掌握本页后,WAL 这条「看不见的河」对你的运维视野将是透明的。

前置知识

建议先阅读:数据库巡检

基础概念 ​

WAL 的生命周期与「谁在等它」 ​

一个 WAL 段文件(默认 16 MB)从产生到删除,会经过多个「消费者检查点」:

text
 产生 ──► 本地持久化 ──┬─► 崩溃恢复可用(checkpoint 之后可循环复用)
                       ├─► 归档进程 archive_command 送往归档存储
                       ├─► walsender 发送给备库(物理/逻辑)
                       └─► 复制槽锚定:槽的 restart_lsn 之前的才允许删除

 删除条件:所有消费者都确认「这段 WAL 我不再需要」
消费者确认机制断开后的表现
checkpointcheckpoint 记录的 REDO 点WAL 段在下一个 checkpoint 后可复用
归档pg_stat_archiver.last_archived_wal归档停住,pg_wal 里 .ready 文件堆积
物理备库pg_stat_replication.replay_lsnwalsender 消失,若无槽则 WAL 按需保留
物理复制槽pg_replication_slots.restart_lsn槽 active=f,pg_wal 无限保留
逻辑订阅槽的 restart_lsn + confirmed_flush_lsn同上,且保留量通常更大

关键认知:没有复制槽时,主库只为「断开的备库」保留一段 WAL(受 wal_keep_size 影响的机会性保留),备库重连后如果需要的段已删就只能重新 basebackup;而有复制槽时,主库会「无限期、无上限」地保留 WAL——这就是为什么失活的槽是积压事故的第一元凶。

LSN:WAL 积压的度量衡 ​

LSN(Log Sequence Number)是 WAL 字节流的地址,形如 0/16B3A48。所有积压计算的本质都是 两个 LSN 之间的字节差:

sql
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), '0/16B3A48');

常用的「坐标锚点」:

函数 / 字段含义
pg_current_wal_lsn()主库当前已写入的 WAL 位置(前沿)
pg_current_wal_flush_lsn()已刷盘位置
pg_stat_replication.sent_lsn已发送给备库的位置
pg_stat_replication.write_lsn / flush_lsn / replay_lsn备库已写/已刷/已回放的位置
pg_replication_slots.restart_lsn槽所要求保留的最早 WAL 位置

三类积压场景对照 ​

场景判定特征第一时间动作
复制槽失活pg_replication_slots.active = f 且无对应 walsender;restart_lsn 长期不动确认下游是否还需要 → 还需要则恢复下游,不需要则删槽
归档失败pg_stat_archiver.failed_count 增长;pg_wal 里大量 .ready 状态文件检查归档存储可达性,修复后归档自动追赶
备库断连pg_stat_replication 无行 / state = 'startup';备库侧报错恢复备库连接;评估是否需要 pg_basebackup 重建

wal_status:槽保留状态的四档水位 ​

pg_replication_slots.wal_status 给出了槽保留的 WAL 处于什么水位(与 max_slot_wal_keep_size 相关):

取值含义风险
reserved保留量在 max_slot_wal_keep_size 之内正常
extended超过了「计划内」保留,靠磁盘空闲空间勉强维持(未设上限时常见)需要关注
unreserved已超过上限,这些 WAL 随时可能被回收槽下游重连后可能需要重建
lost相关 WAL 已被回收,槽已不可用下游必须重建(重新 basebackup / 重建订阅)

语法与用法 ​

监控 pg_wal 目录大小 ​

sql
-- pg_wal 目录总大小(PG10+,超级用户或 pg_monitor)
SELECT pg_size_pretty(sum(size)) AS wal_dir_size,
       count(*)                  AS file_count
FROM pg_ls_waldir();
text
 wal_dir_size | file_count
--------------+------------
 18 GB        |       1152
sql
-- 归档待处理文件(.ready 堆积是归档失败的直接证据)
SELECT count(*) AS ready_files
FROM pg_ls_waldir()
WHERE name LIKE '%.ready';
sql
-- 旧版(PG10 以下)或从文件系统侧监控
-- du -sh $PGDATA/pg_wal/   (bash 侧,见巡检脚本)

计算副本延迟字节 ​

sql
-- 各备库的四级延迟与时间滞后
SELECT application_name,
       client_addr,
       state,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn))   AS sent_lag,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), write_lsn))  AS write_lag,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn))  AS flush_lag,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_lag,
       write_lag  AS write_lag_time,
       replay_lag AS replay_lag_time
FROM pg_stat_replication;
text
 application_name |  client_addr  |   state   | sent_lag | write_lag | flush_lag | replay_lag |    replay_lag_time
------------------+---------------+-----------+----------+-----------+-----------+------------+------------------------
 standby_b        | 10.0.9.12     | streaming | 256 bytes| 256 bytes | 512 bytes | 512 bytes  | 00:00:00.48321
 standby_c        | 10.0.9.13     | streaming | 41 MB    | 41 MB     | 41 MB     | 41 MB      | 00:03:11.20498
(2 rows)

replay_lag 字节列回答「堆积了多少 WAL 还没回放」,replay_lag_time 回答「按现在的速度要追多久」。注意:replay_lag 时间列在备库空闲时可能显示一个历史值并逐渐冻结,字节差永远是实时的,告警以字节差为主、时间为辅。

检查复制槽健康 ​

sql
-- 全量槽体检:是否活跃、保留多少、处于什么水位
SELECT slot_name,
       slot_type,
       active,
       active_pid,
       restart_lsn,
       wal_status,
       safe_wal_size,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;
text
  slot_name  | slot_type | active | active_pid | restart_lsn |  wal_status | safe_wal_size |  retained
-------------+-----------+--------+------------+-------------+-------------+---------------+------------
 standby_b   | physical  | t      |      18234 | 0/9A2B1C00  | reserved    |   91238860800 | 419 MB
 old_staging | physical  | f      |            | 0/8A114500  | extended    |               | 12 GB
(2 rows)

这份输出立刻暴露了问题:old_staging 槽失活且保留了 12 GB WAL——如果它是测试环境遗留的,删掉它即可释放磁盘。

归档健康检查 ​

sql
SELECT archived_count,
       last_archived_wal,
       last_archived_time,
       failed_count,
       last_failed_wal,
       last_failed_time
FROM pg_stat_archiver;
text
 archived_count | last_archived_wal |    last_archived_time     | failed_count | last_failed_wal |    last_failed_time
----------------+-------------------+---------------------------+--------------+-----------------+------------------------
          58211 | 00000001000B9A48  | 2026-09-30 04:00:02.1+08  |          213 | 00000001000B9A2B| 2026-09-30 02:41:19.7+08

failed_count 为 213 且 last_failed_time 在两小时前,说明归档通道经历过故障(或正在故障)。结合 pg_ls_waldir() 中 .ready 文件数量判断当前是否仍在积压。

相关配置参数 ​

conf
# 复制槽保留的 WAL 上限(PG13+),0 或空为不限制
max_slot_wal_keep_size = 50GB

# 没有槽时,为断开的备库保留的 WAL 量(PG13+;更早版本为 wal_keep_segments)
wal_keep_size = 1GB

# 归档模式与命令
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout = 300

max_slot_wal_keep_size 是「防炸磁盘」的兜底保险丝:设为非零后,任何槽能保留的 WAL 都有上限,超限后槽进入 unreserved / lost 状态(WAL 允许被回收),以「槽失效、下游重建」换取「主库活着」。

详细说明 ​

复制槽失活:最危险也最沉默的积压 ​

复制槽的设计目的是「保证 WAL 不被过早回收」,这既是它的价值也是它的风险:槽没有超时概念。一个测试环境的下游实例在三个月前被删除,但主库上的槽还在——这三个月里主库把每一字节 WAL 都留着,直到磁盘写满。

防御分三层:

  1. 上限兜底:设置 max_slot_wal_keep_size,让失控槽最多保留到上限值。代价是超限后槽可能失效,下游需要重建——这是合理的取舍。
  2. 监控告警:对所有槽监控 pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn),并对 active = f 的槽加「失联时长」告警(结合 pg_stat_activity 与主备关系判断是真断连还是已废弃)。
  3. 流程治理:建立「删实例必删槽」的变更检查单;DROP DATABASE 不会自动删除关联槽,逻辑订阅下线时必须显式 pg_drop_replication_slot()。

PG17+ 还提供了 idle_replication_slot_timeout 这类超时参数(长期空闲的槽自动失效),在升级规划时可以评估启用。

归档失败:从 pg_stat_archiver 到 .ready 文件 ​

归档进程(archiver)按 WAL 段为单位调用 archive_command。故障排查路径:

bash
# 1. 看视图确认失败时间与文件
psql -c "SELECT failed_count, last_failed_wal, last_failed_time FROM pg_stat_archiver;"

# 2. 看待归档队列长度(.ready = 待归档,.done = 已归档)
ls $PGDATA/pg_wal/archive_status/ | grep -c '\.ready$'

# 3. 手工执行一次归档命令复现错误(替换为你的 archive_command)
test ! -f /archive/00000001000B9A2B && cp $PGDATA/pg_wal/00000001000B9A2B /archive/

# 4. 常见根因:归档存储挂载丢失、权限变更、磁盘配额满、网络防火墙

归档恢复后,archiver 会自动把积压的 .ready 文件逐个送出,无需人工干预;但如果积压量巨大(几千个段),追赶期间的归档带宽会占用磁盘 I/O,建议低峰处理。归档命令的配置细节(包括 archive_timeout 的取舍)见 PITR 归档配置。

wal_keep_size 与无槽保留的局限 ​

没有复制槽时,主库对「已断开的备库」的 WAL 保留是机会性的:wal_keep_size(PG13+,旧版为 wal_keep_segments)声明主库至少保留这么多 WAL 不复用,供备库重连时追赶。它有两个边界:

  • 保留量是「至少」,实际保留可能更多(取决于 WAL 生成速度与 checkpoint 频率);
  • 备库断连超过 wal_keep_size 对应的 WAL 量后,重连即失败,只能重新 basebackup。

因此生产实践是:正式备库一律配物理复制槽(确定性保留 + 与 pg_stat_replication 联动监控),wal_keep_size 只作为兜底(1~2 GB 常见),不作为主保护。

延迟与积压的区别 ​

「延迟(lag)」与「积压(backlog)」在日常对话中常混用,但运维上值得区分:

  • 延迟是消费者与生产者之间的 LSN 距离,是「进度差」;
  • 积压是「这些差值对应的 WAL 被物理保留下来」的状态,是「资源占用」。

备库延迟 40 MB 而这 40 MB 还在 pg_wal 里等它,既叫延迟也叫积压;归档失败时「消费者(归档存储)」的延迟同样转化为 pg_wal 的积压。所以监控必须同时覆盖两个面:pg_stat_replication 看复制进度、pg_replication_slots + pg_ls_waldir 看资源占用——两者任一异常都指向积压。

safe_wal_size 的预警价值 ​

PG13+ 的 pg_replication_slots.safe_wal_size 直接给出了「这个槽还能安全保留多少 WAL 才会触顶 max_slot_wal_keep_size」的估算。把它做成告警指标最省事:safe_wal_size < 20% × max_slot_wal_keep_size 时预警——无需自己计算差值与上限的关系。

应急处理:drop 槽前的确认清单 ​

磁盘告急时,删掉废弃槽是最快的止血手段,但删错槽 = 下游必须重建。执行前逐条确认:

bash
# 1. 槽的属性:类型、所属库(逻辑槽)、插件
psql -c "SELECT slot_name, slot_type, database, plugin, active, active_pid
         FROM pg_replication_slots;"

# 2. 槽名与现有主机的关系:问问 CMDB / 变更记录,
#    物理槽名通常等于备库的 primary_slot_name
psql -h 备库主机 -c "SHOW primary_slot_name;"

# 3. 失联时长:restart_lsn 对应 WAL 文件的创建时间可粗略估计
ls -l --time-style=long-iso $PGDATA/pg_wal/ | head

确认清单通过后才执行删除:

sql
SELECT pg_drop_replication_slot('old_staging');

pg_replication_slot_advance 的用途与风险 ​

pg_replication_slot_advance(slot_name, target_lsn) 可以把槽的 restart_lsn 手工推进到指定位置,效果是「声明这些 WAL 不再需要」:

sql
-- 把槽推进到当前 WAL 位置(等于放弃槽的追赶义务)
SELECT pg_replication_slot_advance('old_staging', pg_current_wal_lsn());

用途(正当场景):

  • 主备切换后,旧主库上为旧备库建的槽需要推进到新主库位置(配合槽同步工具或手工操作);
  • 逻辑订阅已确认消费到某位置但槽的 confirmed_flush_lsn 因异常未更新时,精确推进以释放 WAL;
  • 测试/演练环境中让槽跳过已知可丢弃的 WAL。

风险(必须刻在脑子里):

  • 物理槽推进是「数据丢弃」:推进后,下游备库重新连接时会发现所需的 WAL 已不存在,直接无法追赶,必须重新 basebackup。绝不能对生产备库的槽做「试一试」式的推进。
  • 逻辑槽推进是「数据丢失」:订阅端会永久丢失被跳过的变更区间,造成静默的数据不一致,且极难事后发现。
  • 推进操作不可逆,执行前把当前 restart_lsn 记录下来(虽然通常也回不去了)。

一句话:advance 是给「确定不再需要那些 WAL」的场景准备的手术刀,不是给「想让积压变小」准备的橡皮擦。

WAL 速率与容量规划 ​

积压监控之外,「这个库每天产生多少 WAL」决定了磁盘容量规划与积压风险的量化评估。WAL 日增量的测量方法:

sql
-- 两次采样点之间生成的 WAL 字节(间隔 24h 的定时任务各记一次)
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), '0/9A000000')::bigint / 1024 / 1024 AS mb_generated;
text
 mb_generated
--------------
        32184
bash
# 粗略替代:pg_stat_wal(PG14+)累计的 WAL 字节数
psql -Atc "SELECT pg_size_pretty(wal_bytes) FROM pg_stat_wal;"
text
 14 TB

容量规划的经验公式:

text
 pg_wal 目录预算 = 基线保留(wal_keep_size + checkpoint 间距内的段数)
                 + max_slot_wal_keep_size(若有槽防护)
                 + 应急缓冲(正常积压峰值的 2 倍)

 归档存储预算 = WAL 日增量 × 保留天数(PITR 窗口)× 冗余系数 1.5

例:某库 WAL 日增量 32 GB、PITR 窗口 7 天 → 归档存储约 32 × 7 × 1.5 ≈ 336 GB;pg_wal 预算在设置 max_slot_wal_keep_size = 50GB 后按 80 GB 规划。把这两个数字写进容量文档,并在 WAL 速率显著变化(新业务上线、批量任务变更)时重算——WAL 速率是积压所有阈值的地基,地基变了阈值全部要校准。

积压应急的优先级决策 ​

磁盘告急时的处置顺序(先救最致命的):

优先级动作说明
1确认 pg_wal 离盘满还有多久df -h 与 WAL 日增速率相除,< 2 小时则升级为 P1
2找出积压大头:槽 or 归档pg_replication_slots 保留分布 vs .ready 文件数
3废弃槽确认后删除本页示例一流程;这是唯一能「立即释放 GB 级」的动作
4归档通道修复或临时扩容修复前可用更大的本地挂载暂缓 .ready 压力(改 archive_command 到本地暂存盘,待修复后批量补送)
5兜底手段:降低积压增速停掉非关键的批量任务、延迟大事务,为处理争取时间
6最后手段(慎用)调低 max_slot_wal_keep_size 让失控槽失效——必须有下游重建的预案才允许

第 6 条值得展开:调小 max_slot_wal_keep_size 触发槽失效属于「主动截肢」,执行前必须确认失效的下游可以重建(物理备库重新 basebackup、逻辑订阅重建订阅),并把决策记录到变更单。它是磁盘将满且找不到可删槽时保住主库的最后选择。

pg_stat_wal_receiver:备库侧的接收视角 ​

WAL 积压的另一半现场在备库。pg_stat_wal_receiver 描述了备库上 WAL 接收进程(wal receiver)的状态,是备库巡检的固定项:

sql
-- 在备库执行
SELECT status,
       sender_host,
       sender_port,
       received_lsn,
       last_msg_send_time,
       last_msg_receipt_time,
       latest_end_lsn
FROM pg_stat_wal_receiver;
text
  status  |  sender_host  | sender_port | received_lsn |   last_msg_receipt_time
----------+---------------+-------------+--------------+---------------------------
 streaming| 10.0.9.11     |        5432 | 0/9B2C1A00   | 2026-09-30 04:12:33.2+08

判读要点:status = streaming 为正常;last_msg_receipt_time 距今超过 30 秒说明心跳中断(网络或主库问题);主库侧 pg_stat_replication 看不到对应行而备库侧 wal receiver 仍在,通常意味着「连接活着但复制协商卡住」,这类「半死」状态比彻底断连更难发现——两侧视图交叉巡检才能覆盖。

告警阈值设计 ​

指标预警严重说明
pg_wal 目录大小> 50% 上限(如 10 GB)> 80% 上限(如 16 GB)结合磁盘总量定绝对值
单槽保留量(restart_lsn 差值)> 10 GB> 30 GB 或 > max_slot_wal_keep_size 的 80%按业务 WAL 速率校准
active = f 的槽失联 > 10 分钟失联 > 1 小时疑似废弃槽立即走确认流程
wal_status= extended∈ {unreserved, lost}直接映射到告警级别
归档 failed_count 增量> 0连续增长 5 分钟配合 .ready 文件数
备库 replay_lag 字节> 100 MB> 1 GB以字节为主,时间为辅
归档积压 .ready 数> 20> 100归档追赶能力的先行指标

阈值没有普适答案,校准方法:先记录日常 WAL 日增量与峰值速率,把「严重」阈值定为「按当前速率,磁盘还能撑 6~12 小时」的量级,给应急响应留出时间。

实战示例 ​

示例一:一次完整的积压应急处理 ​

周四 15:40 收到磁盘告警:数据盘使用率 91%。进入主库按标准流程处置。

第一步:定位积压构成。

sql
-- pg_wal 目录多大?待归档多少?
SELECT pg_size_pretty(sum(size)) AS wal_size, count(*) AS files
FROM pg_ls_waldir();

SELECT count(*) FILTER (WHERE name LIKE '%.ready')  AS ready,
       count(*) FILTER (WHERE name LIKE '%.done')   AS done
FROM pg_ls_waldir();

-- 槽的保留分布
SELECT slot_name, active, wal_status,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;
text
 wal_size | files
----------+-------
 78 GB    |  4992
(1 row)

 ready | done
-------+------
  1280 | 3712
(1 row)

  slot_name  | active | wal_status | retained
-------------+--------+------------+----------
 standby_b   | t      | reserved   | 2 GB
 ci_clone    | f      | extended   | 61 GB
(2 rows)

第二步:三类场景比对。.ready 有 1280 个 → 归档也在积压;ci_clone 槽失活且保留 61 GB → 主因找到。两路并查:

sql
-- 归档何时开始失败?
SELECT failed_count, last_failed_wal, last_failed_time FROM pg_stat_archiver;
text
 failed_count | last_failed_wal |    last_failed_time
--------------+-----------------+------------------------
          1280| 00000001000B8A00| 2026-09-25 09:12:44+08

归档从 9 月 25 日起持续失败(归档存储磁盘满,另开工单),主因是 ci_clone 槽。

第三步:处置 ci_clone 槽。按「drop 槽前确认清单」核查:CI 环境的克隆实例在两周前的变更单里已下线(CMDB 可查),槽是遗留物:

sql
-- 确认失联时长(WAL 文件时间可佐证)后删除
SELECT pg_drop_replication_slot('ci_clone');
text
 pg_drop_replication_slot
--------------------------
 t
(1 row)
sql
-- 复查磁盘
SELECT pg_size_pretty(sum(size)) AS wal_size FROM pg_ls_waldir();
text
 wal_size
----------
 17 GB
(1 row)

61 GB 立即释放,磁盘使用率回落到 78%,止血完成。

第四步:处理归档积压。归档存储已扩容,archiver 自动开始追赶:

bash
# 观察 .ready 队列下降
watch -n 30 "ls $PGDATA/pg_wal/archive_status/ | grep -c '\.ready$'"
# 1280 -> 864 -> 411 -> 96 -> 0 (约 40 分钟清空)

# 验证归档恢复
psql -c "SELECT archived_count, failed_count, last_archived_time FROM pg_stat_archiver;"

第五步:补防护,防复发:

sql
ALTER SYSTEM SET max_slot_wal_keep_size = '50GB';
SELECT pg_reload_conf();
bash
# 告警规则补充:
# 1. 槽保留量 > 30 GB 严重告警
# 2. active=f 持续 1 小时告警
# 3. .ready 文件数 > 100 告警

示例二:监控 SQL 组装成采集项 ​

把核心指标封装成一条查询,方便 Prometheus exporter / 自研采集器直接调用:

sql
SELECT 'wal_dir_bytes'   AS metric, sum(size)::text AS value FROM pg_ls_waldir()
UNION ALL
SELECT 'ready_files',    count(*)::text FROM pg_ls_waldir() WHERE name LIKE '%.ready'
UNION ALL
SELECT 'inactive_slots', count(*)::text FROM pg_replication_slots WHERE NOT active
UNION ALL
SELECT 'max_slot_retained_bytes',
       max(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn))::text
FROM pg_replication_slots
UNION ALL
SELECT 'min_replay_lag_bytes',
       min(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn))::text
FROM pg_stat_replication;
text
          metric           |     value
---------------------------+----------------
 wal_dir_bytes             | 18253611008
 ready_files               | 0
 inactive_slots            | 0
 max_slot_retained_bytes   | 439814656
 min_replay_lag_bytes      | 419430
(5 rows)

示例三:备库长时间断连后的恢复决策 ​

备库 C 断连 6 小时后网络恢复。作为主库 DBA,先判断它能不能直接追上:

sql
-- 主库:C 的槽还留着多少 WAL?
SELECT slot_name, active, wal_status,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
WHERE slot_name = 'standby_c';

-- C 的断连起点在哪:比较槽的 restart_lsn 与 pg_wal 里最旧文件的段号
SELECT pg_walfile_name(restart_lsn) FROM pg_replication_slots WHERE slot_name = 'standby_c';
bash
# 主库:pg_wal 里最旧的段文件
ls -1 $PGDATA/pg_wal/ | grep -E '^[0-9A-F]{24}$' | sort | head -1

若 C 需要的段仍在(wal_status = reserved),C 重连后自动从 restart_lsn 开始追赶,监控其 replay_lag 下降即可;若已 lost(所需段被回收),走重建:

bash
# 备库 C 上:停库 → 清空数据目录 → 重新 basebackup(参见物理备份章节)
pg_ctl -D /data/pg17 stop
rm -rf /data/pg17/*
pg_basebackup -h 主库 -U repl -D /data/pg17 -X stream -C -S standby_c -R
pg_ctl -D /data/pg17 start

-C -S standby_c 在主库重建同名槽,-R 自动生成 standby.signal 与连接配置。物理备份与恢复细节参见 pg_basebackup 物理备份。

示例四:主备切换后的槽位维护 ​

提升备库 B 为新主库后,B 上没有原备库 C 的槽,而旧主库上 C 的槽还在(且指向已过时的位置)。标准动作:

bash
# 新主库 B:为 C 建槽(或由 pg_basebackup -C / 槽同步机制完成)
psql -h 新主库B -c "SELECT pg_create_physical_replication_slot('standby_c');"

# C 的 postgresql.conf 指向 B 后重启/reload,确认追上
psql -h 新主库B -c "SELECT application_name, state,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag
       FROM pg_stat_replication;"

# 旧主库(降级为备)上的旧槽处理:确认无人使用后删除或按计划重建
psql -h 旧主库A -c "SELECT slot_name, active FROM pg_replication_slots;"

这个场景也是 pg_replication_slot_advance 的正当用武之地:如果旧主库很快会被重新拉平,其上遗留槽的 restart_lsn 可在确认后推进,避免它保留大量已被集群「抛弃」的 WAL。

注意事项 ​

pg_ls_waldir() 需要超级用户或 pg_monitor 角色。 巡检与监控采集账号按巡检一节的方式授权,否则采集会静默失败(查询报错而不是返回空)。

pg_stat_replication 只在主库上有行。 监控备库延迟要在主库执行;级联复制下中间层的延迟要在其上游节点分别观察。备库侧能看的是 pg_stat_wal_receiver(接收链路健康)。

replay_lag 时间列会「冻结」。 备库空闲(没有新 WAL)时,该列显示的是最后一次有 WAL 时的时间差,且会随空闲时间显示为 NULL 或保持旧值。字节差(pg_wal_lsn_diff)不受影响,告警以字节差为准。

删槽 ≠ 删下游。 删除主库上的槽不会对下游实例做任何事;反过来,下线下游实例时若不删槽,就是本章演示的事故。把「删实例 → 删槽」写进下线流程的检查单。

max_slot_wal_keep_size 是用「槽失效」换「主库存活」的取舍。 设置它之前与所有槽的 owner(备库管理员、订阅方)对齐:超限意味着下游重建。设为 0(不限)的集群等于没有保险丝。

wal_status = lost 的槽只能重建。 此时 restart_lsn 指向的 WAL 已被回收,下游无论是物理备库还是逻辑订阅都必须重新初始化。告警要在 extended / unreserved 阶段就抓住它,不要等到 lost。

归档追赶也会打满磁盘 I/O。 长时间归档中断后恢复,archiver 会全速补归档,可能与业务争抢 I/O;积压量特别大时可在业务低峰临时放慢(例如调整归档存储的写入节流),或评估直接重新做一次全量备份来「对齐」归档基线。

逻辑订阅的槽保留量比物理备库更大。 逻辑解码需要保留事务完整的 WAL 窗口(包括未提交大事务的全部 WAL),同一断连时长下逻辑槽的保留量可能数倍于物理槽。对逻辑订阅设置更严格的 max_slot_wal_keep_size 预算。

最佳实践 ​

  1. 所有正式下游一律配复制槽,wal_keep_size 只作兜底。 槽提供确定性保留与可监控性(restart_lsn、wal_status),无槽部署无法预警「备库断连多久后会失联」。
  2. 实例级设置 max_slot_wal_keep_size 并纳入基线配置。 这是防止「废弃槽炸磁盘」的最后一道防线;上限按「磁盘余量 / 槽数」或业务 WAL 速率的两倍设定。
  3. 监控三件套固定采集:pg_wal 大小、.ready 文件数、每槽保留量。 三者分别覆盖总水位、归档通道、槽通道,任何一路异常都有独立告警。
  4. active = f 的槽必须有「失联时长」告警与处置 SLA。 失联 1 小时内确认(下游恢复或废弃确认),超时升级人工——沉默的槽是最大的事故源。
  5. 以字节差为主、时间列为辅度量副本延迟。 pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) 是唯一永远实时的指标;阈值参考本页告警表并按 WAL 速率校准。
  6. 归档失败要盯 pg_stat_archiver.failed_count 增量与 .ready 队列长度。 archive_command 的失败不会抛给业务,只有主动监控能发现;归档配置细节见 PITR 归档配置。
  7. drop 槽走确认清单:类型 → 归属 → CMDB → 失联时长。 四项核对全部通过才允许 pg_drop_replication_slot();操作记录进变更单。
  8. 把 pg_replication_slot_advance 当手术刀:只在切换后槽位维护、废弃基线确认等明确场景使用。 对任何「还在使用中」的槽执行 advance 都等于制造数据丢失或强制重建。
  9. 建立「删实例必删槽」「重建备库先看槽状态」的变更检查单。 槽的生命周期管理是流程问题,不是技术问题。
  10. 每周巡检核对 wal_status 全表。 extended 出现即预警,unreserved 出现即立项处置,不要给 lost 留机会。
  11. 把本页监控 SQL 封装成采集项接入告警平台。 与巡检一节的方法论衔接:巡检兜底发现,实时监控负责秒级响应,两者共用同一套判据。

下一节

继续学习:版本升级概述

参考链接 ​