# 保留型故障的安全路由

LLMS 索引： [llms.txt](/llms.txt)

---

保留型事故的第一原则是：

> 先证明“谁还需要哪段历史”，再决定是恢复消费者、迁移恢复来源，还是释放这项需要。

一条复制槽、一个 xmin 或一批 WAL 文件本身不是垃圾。它们是恢复、复制、快照或事务
协议的状态。未经 owner 与恢复链确认的“清理”，可能把空间问题变成不可恢复的数据
问题。

## 34.6.1 XID：检查 `backend_xmin`、复制槽 `xmin` 与 `pg_prepared_xacts` {#item-34-6-1}

### 先列出所有可能的 horizon owner

活动 backend：

```sql
SELECT pid,
       datname,
       usename,
       application_name,
       state,
       xact_start,
       backend_xid,
       backend_xmin,
       wait_event_type,
       wait_event
FROM pg_stat_activity
WHERE backend_xid IS NOT NULL
   OR backend_xmin IS NOT NULL
ORDER BY xact_start NULLS LAST;
```

复制槽：

```sql
SELECT slot_name,
       slot_type,
       database,
       active,
       xmin,
       catalog_xmin,
       restart_lsn,
       inactive_since,
       invalidation_reason
FROM pg_replication_slots
ORDER BY slot_name;
```

两阶段事务：

```sql
SELECT transaction,
       gid,
       prepared,
       owner,
       database
FROM pg_prepared_xacts
ORDER BY prepared;
```

再看 database/relation freeze age：

```sql
SELECT datname,
       age(datfrozenxid) AS xid_age,
       mxid_age(datminmxid) AS multixact_age
FROM pg_database
ORDER BY xid_age DESC;
```

这些视图回答的是不同问题：

- `backend_xmin`：该 backend 当前快照仍可能看到多老的版本；
- slot `xmin`：消费者需要的数据行版本边界；
- `catalog_xmin`：逻辑解码需要的 catalog 版本边界；
- prepared XID：已经 `PREPARE TRANSACTION`、等待外部决议的事务；
- `datfrozenxid`：数据库中尚未冻结事务的保守下界。

### 不要把最老 PID 自动当成罪魁

一个长 session 未必持有 xmin；一个短暂但 prepared 的事务可能没有 session 却长期
持锁。logical slot 的 `catalog_xmin` 也可能成为 catalog vacuum 的约束。先把每个边界
映射到：

```text
owner
business or replication purpose
last successful progress
expected outage/retention window
recoverability if released
approved decision maker
```

处置路线：

| 保留者 | 安全路线 |
|---|---|
| 应用长事务 | 联系 owner，停止新工作，评估 cancel/terminate 与补偿 |
| prepared transaction | 与 transaction manager/ledger 对账后 commit 或 rollback |
| logical slot | 恢复 consumer，或从新起点重建并明确数据缺口 |
| freeze 落后 | 修复 blocker/资源后执行受控 vacuum/freeze |
| 无法识别 | 保持证据，升级，不释放 |

完整的 XID、freeze 与膨胀处理见[第 28 章](/vacuum-freeze-bloat/)。

## 34.6.2 WAL 撑盘：检查归档失败、复制槽和未完成备份 {#item-34-6-2}

### `pg_wal` 大小不是根因

WAL 目录可以因为正常高写入暂时变大，也可以因为保留者不推进持续增长。先记录：

```sql
SELECT pg_current_wal_lsn() AS current_lsn,
       pg_wal_lsn_diff(
         pg_current_wal_lsn(),
         '0/0'::pg_lsn
       ) AS absolute_lsn_bytes;

SELECT slot_name,
       slot_type,
       active,
       restart_lsn,
       pg_wal_lsn_diff(
         pg_current_wal_lsn(),
         restart_lsn
       ) AS retained_wal_bytes,
       wal_status,
       safe_wal_size,
       invalidation_reason
FROM pg_replication_slots
ORDER BY retained_wal_bytes DESC NULLS LAST;

SELECT archived_count,
       failed_count,
       last_archived_wal,
       last_archived_time,
       last_failed_wal,
       last_failed_time
FROM pg_stat_archiver;
```

同时检查：

```text
physical replica receive/replay progress
logical consumer confirmed progress
archive command/repository health
base backup / restore / WAL summarization activity
checkpoint timing
WAL generation rate by workload
filesystem free bytes and inode headroom
```

`pg_stat_archiver.failed_count` 是累计量；单次历史失败不证明当前仍坏。需要看最近成功、
最近失败和时间窗增量。slot `active=true` 也只说明当前有人连接，不说明 `restart_lsn`
正在推进。

### 未完成备份也是恢复链的一部分

备份过程可能需要一段 WAL 才能形成一致恢复点。事故中贸然停止备份、删除 staging
或释放 WAL，可能让本来可用的恢复副本失效。记录：

```text
backup run id / start LSN / stop LSN
repository and archive confirmation
base backup progress
restore validation state
current RPO source
alternative healthy backup/replica
```

若空间即将耗尽，可以同时减少非关键写入，降低 WAL 增长率；这只是争取时间，不是
修复 retention owner。

### slot 的有限上限也不是无损方案

`max_slot_wal_keep_size` 可以限制 checkpoint 时允许 replication slot 保留的 WAL；
超过可用范围时 slot 可能不再可继续使用，`wal_status`/`invalidation_reason` 会反映
状态。它是防止磁盘无限增长的风险取舍，不保证 consumer 无损恢复。配置前必须明确：

```text
consumer maximum outage
WAL generation envelope
alert lead time
reseed procedure
accepted data-loss semantics
```

## 34.6.3 绝不手工删除 `pg_wal`；保护现场后转 ch21/ch28/ch35 {#item-34-6-3}

### 为什么文件看起来“旧”也不能删

PostgreSQL 自己依据 checkpoint、recovery、归档和复制需要管理 WAL segment。文件名
顺序不能告诉操作者某段是否仍被 crash recovery、standby、backup 或 timeline history
需要。运行中用 `rm` 删除 `pg_wal`：

- 不会更新 control/catalog/slot 状态；
- 可能让当前实例在 crash recovery 时缺日志；
- 可能让 replica、PITR 或 backup 无法继续；
- 会破坏最重要的事故证据；
- 当前进程暂时继续运行，也不能证明下次 restart 可恢复。

正确做法是：

```text
reduce noncritical WAL generation
preserve SQL + filesystem + archive evidence
identify exact retention owner
repair archive/consumer or establish a new recovery source
release only an exact, authorized object through PostgreSQL
verify recovery chain and filesystem headroom
```

### 路由到正确章节

- XID、vacuum、freeze、膨胀：转[第 28 章](/vacuum-freeze-bloat/)；
- 归档、备份、恢复链：转[第 21 章](/backup-recovery/)；
- 已发生文件缺失、checksum/页/索引异常：保护现场，转
  [第 35 章](/data-rescue-forensics/)；
- 错删/误写但物理集群健康、需要时间点恢复：结合
  [第 32 章](/pitr/)。

每次转交都带：

```yaml
facts:
  system_identifier_timeline: ...
  current_and_retained_lsn: ...
  owner_consumer: ...
  archive_backup_state: ...
  growth_rate_time_to_full: ...
actions_already_taken: [...]
unknowns: [...]
forbidden_actions:
  - manual delete pg_wal
  - drop unknown slot
```

## 34.6.4 本章的一般止血动作不构成保留型修复 {#item-34-6-4}

### 常见伪修复

| 动作 | 可能短期效果 | 为什么没修复保留边界 |
|---|---|---|
| cancel 普通慢查询 | 降低 CPU/I/O | 不一定命中持 xmin 的事务 |
| 限制新连接 | 减少 flow | slot/归档边界仍不推进 |
| 增加磁盘 | 延后满盘 | owner 与恢复链仍旧失效 |
| 重启数据库 | 清部分 session | prepared xact/slot/归档问题仍在，且增加恢复风险 |
| drop 所有 inactive slot | 快速释放 WAL | 破坏未知 consumer 的恢复能力 |
| 删除 WAL 文件 | 目录变小 | 数据库状态未修复，恢复链被破坏 |

增加磁盘在硬故障逼近时可以是合法的**时间购买动作**，但报告必须写：

```text
temporary headroom gained
new estimated time to full
retention owner unchanged
permanent repair owner/deadline
rollback or capacity reconciliation
```

### 保留型成功标准

不是“磁盘百分比下降”，而是：

```text
exact retention owner identified
oldest required horizon is advancing or deliberately re-established
consumer/archive/backup has a valid recovery path
released capability and accepted loss are recorded
WAL/XID growth rate returns inside envelope
replicas and PITR validation pass
temporary storage/traffic controls are reconciled
```

如果唯一能说的是“删完之后 PostgreSQL 还在运行”，事故尚未被安全恢复。

---

[上一节：流量型止血动作](../05/) · [返回本章目录](../) · [下一节：平台级流量控制与证据](../07/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
