文章目录PG 孤儿物理复制槽残留导致 pg_wal 膨胀 19G 故障分析1. 故障现象1.1 磁盘空间异常1.2 复制槽查询发现异常2. 排查过程2.1 关键指标解读2.2 排除其他可能2.3 复制槽基础原理排查依据3. 根因分析3.1 事故链3.2 缺陷定性3.3 三个放大隐患4. 解决方案4.1 删除前安全检查必做4.2 执行删除4.3 验证结果4.4 若需主动加速回收5. 经验总结5.1 排查思路可复用5.2 常用运维命令5.3 预防措施5.4 关键结论PG 孤儿物理复制槽残留导致 pg_wal 膨胀 19G 故障分析项目内容故障日期2026-09-17故障现象服务器磁盘被pg_wal占用19G正常应为几十 MB存在撑满磁盘、拖死主库的风险影响范围停车场环境主库所在服务器pg_wal持续膨胀会最终写满磁盘导致数据库不可用根因配置 2 个从机时底层在主库创建了 2 个物理复制槽命名含从机 IP解除主从关系时未删除槽叠加环境从 192 网段切换到 172 网段旧槽再无消费者成为孤儿槽并强制保留 WAL解决方案确认无消费者后用pg_drop_replication_slot()删除 2 个孤儿槽pg_wal由 19G 降至 17M环境信息openEuler PostgreSQL 15.18PolarDB 15.18.5.0 内核PGDATA/postgresql/pgdata1. 故障现象1.1 磁盘空间异常pg_wal目录占用异常达 19G[rootopenEuler ~]# du -sh /postgresql/pgdata/pg_wal19G /postgresql/pgdata/pg_wal[rootopenEuler ~]# du -sh /postgresql/pgdata/1.7G /postgresql/pgdata/# 删除前 pgdata 总量被 pg_wal 占据1.2 复制槽查询发现异常postgres# select * from pg_replication_slots;slot_name|plugin|slot_type|datoid|database|temporary|active|active_pid|xmin|catalog_xmin|restart_lsn|confirmed_flush_lsn|wal_status|safe_wal_size|two_phase----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------repl_replication_slot_192_168_0_96||physical|||f|f||||0/C37EFE8||extended||f repl_replication_slot_192_168_0_99||physical|||f|f||||0/C350168||extended||f repl_replication_slot_172_168_0_99||physical|||f|t|1009698|||C/3C29A000||reserved||f repl_replication_slot_172_168_0_96||physical|||f|t|1009542|||C/3C29A000||reserved||f(4rows)4 个物理复制槽分成泾渭分明的两组槽名activerestart_lsnwal_status判定repl_replication_slot_192_168_0_96f0/C37EFE8extended孤儿槽192 旧网段repl_replication_slot_192_168_0_99f0/C350168extended孤儿槽192 旧网段repl_replication_slot_172_168_0_99tC/3C29A000reserved正常在用172 现网段repl_replication_slot_172_168_0_96tC/3C29A000reserved正常在用172 现网段2. 排查过程2.1 关键指标解读1**active**字段——判断槽有没有消费者active表示该槽当前是否正被 walsender 进程占用即active_pid IS NOT NULL。192 的两个槽activef且active_pid为空说明没有任何复制连接在消费它们。2**restart_lsn**字段——判断槽是否冻结192 槽的restart_lsn停留在0/...逻辑段号 0172 槽的restart_lsn已在C/...逻辑段号 121 个 WAL 逻辑段 256 个 16MB 段文件 ≈ 4GB两者相差多个逻辑段说明 192 这两个槽从极早的位置起就再没被消费过完全对应环境从 192 网段切到 172 网段后就没人认领了。3**wal_status**字段——WAL 膨胀的直接铁证PG 13 引入该字段取值含义取值含义reservedWAL 保留量在max_wal_size范围内正常**extended**保留量已超出**max_wal_size**但因槽存在被迫继续保留unreserved不再保留 WALrestart_lsn之前的 WAL 可能已被删除lost槽已失效需要重建192 槽是extended——这就是**pg_wal**被撑到 19G 的直接原因而 172 槽是reserved属正常范围。2.2 排除其他可能排查项结果结论是否有 walsender 连接 192 槽activef、active_pid为空无消费者可安全删除172 槽是否正常activetrestart_lsn持续推进现网段从机在用不可动项目打包目录是否有槽管理代码product/Linux/x86_64/{bin,script}搜索repl_replication_slot/physical_replication_slot/pg_drop_replication_slot均无命中槽由底层业务代码运行时动态创建不经postgresql.conf模板2.3 复制槽基础原理排查依据复制槽是什么复制槽replication slot是主库上的持久化对象作用是向主库承诺保留从该位置起的 WAL直到消费者确认已接收。存储位置$PGDATA/pg_replslot/slot_name/state生命周期显式创建pg_create_physical_replication_slot()或pg_basebackup -S ... -C只有显式**pg_drop_replication_slot()**才会删除PG 没有任何备库不连了就自动删槽的机制为什么槽会导致 WAL 堆积槽存在 → 主库必须保留 restart_lsn 之后的所有 WAL ↓ 备库不再消费 → restart_lsn 不再推进 ↓ WAL 无限累积 → pg_wal 持续膨胀 → 磁盘写满 → 主库挂掉两个易混淆视图的区别pg_stat_replicationpg_replication_slots反映什么当前活跃的 walsender 连接会话级已创建的复制槽持久化对象何时有记录有人连上来做流复制就有只有显式创建过槽才有何时消失备库断开即消失只有 drop 才消失无槽流复制有记录空注无槽流复制未配primary_slot_name虽然也能跑通但没有 WAL 保留保障仅靠wal_keep_size默认 0兜底主库 checkpoint 后可能回收掉备库未接收的 WAL导致备库报requested WAL segment ... has already been removed而断流。3. 根因分析3.1 事故链1. 停车场环境配置 2 个从机 2. 底层业务代码在主库创建 2 个物理复制槽 命名规则repl_replication_slot_从机IP下划线形式 → repl_replication_slot_192_168_0_96 / repl_replication_slot_192_168_0_99 3. 环境从 192 网段切换到 172 网段新建 2 个槽 → repl_replication_slot_172_168_0_96 / repl_replication_slot_172_168_0_99 4. 解除原主从关系时未执行 pg_drop_replication_slot() 5. 192 两个槽失去消费者 → activefrestart_lsn 永久停滞 6. PG 仍按承诺强制保留 WAL → wal_statusextended超出 max_wal_size 7. WAL 持续累积 → pg_wal 膨胀至 19G3.2 缺陷定性资源创建与释放不对称底层创建了持久化对象复制槽解除主从关系时没有对应的销毁动作。这是代码缺陷而非运维误操作。3.3 三个放大隐患隐患说明后果槽名内嵌 IP命名规则repl_replication_slot_IP把网络地址编进了对象名换网段/改 IP 后旧槽名无法被自动识别清理必然残留备库侧**primary_slot_name**未清理若只删了主库的槽、没清备库postgresql.auto.conf中的primary_slot_name该实例下次作为备库启动会报replication slot xxx does not exist挂不上复制无监控告警孤儿槽从产生到磁盘被撑到 19G 期间零告警只能被动发现等到磁盘告警才暴露4. 解决方案4.1 删除前安全检查必做-- 1) 确认目标槽无消费者SELECTslot_name,slot_type,active,active_pid,restart_lsn,pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),restart_lsn))ASretained_walFROMpg_replication_slotsWHEREslot_nameIN(repl_replication_slot_192_168_0_96,repl_replication_slot_192_168_0_99);-- 2) 对照当前实际连接SELECTapplication_name,client_addr,state,sync_stateFROMpg_stat_replication;判定标准active f且active_pid为空pg_stat_replication中无对应连接 → 可安全删除。若槽为active tpg_drop_replication_slot()会直接报错replication slot xxx is active for PID nnn不会误删。4.2 执行删除SELECTpg_drop_replication_slot(repl_replication_slot_192_168_0_96);SELECTpg_drop_replication_slot(repl_replication_slot_192_168_0_99);4.3 验证结果删除后仅剩 2 个在用的槽postgres# select * from pg_replication_slots;slot_name|plugin|slot_type|datoid|database|temporary|active|active_pid|restart_lsn|wal_status|two_phase--------------------------------------------------------------------------------------------------------------------------------------------repl_replication_slot_172_168_0_99||physical|||f|t|1009698|C/3C3C8000|reserved|f repl_replication_slot_172_168_0_96||physical|||f|t|1009542|C/3C3C8000|reserved|f(2rows)注意172 槽的restart_lsn由C/3C29A000推进到C/3C3C8000确认活跃槽工作正常。**pg_wal**空间快速回落[rootopenEuler ~]# du -sm /postgresql/pgdata/pg_wal/17265→15873→15457→6529→6465→6177→3921→3217→2673→1377→17# 数十秒内由 17GB 降至 17MB[rootopenEuler ~]# du -sh /postgresql/pgdata/pg_wal/17M /postgresql/pgdata/pg_wal/[rootopenEuler ~]# du -sh /postgresql/pgdata/1.7G /postgresql/pgdata/回收为何这么快PG 在pg_drop_replication_slot()之后会主动触发一次 checkpointCHECKPOINT_IMMEDIATE | FORCE | WAIT因此 WAL 回收立即启动无需等待常规checkpoint_timeout周期。执行du期间出现du: cannot access .../0000000100000008000000CA: No such file or directory属正常现象——是du扫描目录时 WAL 文件正被 unlink。4.4 若需主动加速回收CHECKPOINT;-- 手动触发回收SELECTcount(*)FROMpg_ls_waldir();-- 观察 WAL 文件数量若删槽 checkpoint 后pg_wal仍不下降需排查另一条链路SHOWarchive_mode;-- on 且归档积压时 WAL 也不会删SELECT*FROMpg_stat_archiver;5. 经验总结5.1 排查思路可复用**pg_wal**异常膨胀先查复制槽SELECT * FROM pg_replication_slots;——槽是 WAL 堆积的头号嫌疑看**active**分组activef的是孤儿槽activet的是在用槽看**wal_status**定性extended 已被迫超量保留 WAL是膨胀的直接证据看**restart_lsn**是否推进停滞说明消费者已消失用**pg_stat_replication**交叉验证为空即无 walsender 接入可放心清理删槽前必须确认无消费者删除后 PG 会自动 checkpoint 回收5.2 常用运维命令-- 查看所有槽及关键状态SELECTslot_name,slot_type,active,active_pid,restart_lsn,wal_status,pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),restart_lsn))ASretained_walFROMpg_replication_slotsORDERBYactive;-- 查看当前活跃复制连接SELECTapplication_name,client_addr,state,sync_state,sent_lsn,replay_lsnFROMpg_stat_replication;-- 查看 WAL 总量与文件数SELECTpg_size_pretty(sum(size))FROMpg_ls_waldir();SELECTcount(*)FROMpg_ls_waldir();-- 幂等清理按前缀批量删避免单个槽不存在导致流程中断SELECTpg_drop_replication_slot(slot_name)FROMpg_replication_slotsWHEREslot_nameLIKErepl_replication_slot_%ANDactivefalse;-- 触发回收CHECKPOINT;# 查看 pg_wal 占用du-sh$PGDATA/pg_wal5.3 预防措施层次措施说明代码修复根本解除主从关系时同步pg_drop_replication_slot()资源创建/释放必须对称建议提问题单推动底层修复命名优化槽名不要内嵌 IP改用稳定标识从机序号/UUID换网段/改 IP 后不会产生无法识别的孤儿槽清理幂等清理逻辑按条件删WHERE slot_name LIKE ... AND active falsepg_drop_replication_slot()对不存在的槽会报错需容错同步清理备库配置解除主从时一并清理备库primary_slot_name否则该实例下次作备库会报replication slot ... does not exist监控告警① 孤儿槽activef且restart_lsn停滞超 N 小时②wal_status extended即告警③pg_wal目录容量本次从产生到 19G 全程零告警防御性配置评估设置max_slot_wal_keep_size默认-1不限制给单个槽的 WAL 保留量设上限防止单个孤儿槽撑爆磁盘流程固化解除主从标准清单删槽 → 清备库primary_slot_name→ 清standby.signal→CHECKPOINT回收避免遗漏5.4 关键结论复制槽是向主库承诺保留 WAL的持久化对象PG 不会自动清理。只要槽失去消费者又不删除它就会持续拽住 WAL 不放wal_status会变成extended并最终撑满磁盘。 因此凡是有槽的实例必须建立孤儿槽巡检机制凡是有槽创建逻辑的代码必须有对应的删除逻辑。