34.1 第一动作:流量型还是保留型
资源告警出现后,先不要问“删什么”或“重启谁”,而要问:
当前资源是被正在到达和执行的工作消耗,还是被一个不能前进的历史边界 保留?
这不是给故障贴标签,而是选择安全动作。流量型压力需要减少进入量、并发或单项成本; 保留型压力需要找到保留者及其 owner。两类证据都成立时,先处理即将触发的硬失败, 同时保留另一条根因链;两类都不成立时,保持 unknown。
34.1.1 流量增长、慢查询、锁与连接导致的竞争
流量型的共同结构
下面四种表象最后都会形成“到达大于完成”:
| 放大源 | 到达侧变化 | 完成侧变化 |
|---|---|---|
| 业务流量增长 | 请求数增加 | 单次成本可能不变 |
| 慢查询或坏计划 | 请求数不变 | 每次占用 CPU/I/O/连接更久 |
| 锁竞争 | 等待工作继续占连接 | 有效并行度下降 |
| 连接风暴 | 建连、认证、backend 创建增加 | 正常查询拿不到 admission |
若每秒进入 $\lambda$ 个工作、完成 $\mu$ 个工作,持续满足 $\lambda>\mu$,队列就增长。 这里的工作可以是 HTTP 请求、池等待者、数据库 session、正在运行的 statement 或磁盘 I/O。只看 PostgreSQL 的 active session 会漏掉在应用池和代理前排队的请求。
先把同一 UTC 窗口的证据放在一起:
SELECT application_name,
state,
wait_event_type,
wait_event,
count(*) AS sessions,
max(clock_timestamp() - coalesce(xact_start, query_start))
AS oldest_age
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1, 2, 3, 4
ORDER BY sessions DESC;再对照:
application arrival / timeout / retry
pool waiting clients / server connections
proxy accept / queue / backend health
PostgreSQL active / idle-in-transaction / Lock waits
host run queue / memory pressure / device latencystate='active' 不表示正在使用 CPU;PostgreSQL 文档明确指出 state 与 wait_event
相互独立。active 且 wait_event_type='Lock' 的 backend 正在执行语句,但实际被阻塞。
这也是为什么“active 数很多”必须继续拆成 running、lock wait、I/O wait 与 client wait。
判定成立的最低条件
把问题判为 flow,至少应看到一条能够闭合的因果链:
arrival/retry increases
-> admission or execution concurrency increases
-> queue/wait/rejection grows
-> completion rate or user success falls仅凭 CPU 90% 不够。CPU 高也可能是 checkpoint 后的恢复工作、压缩、备份或一个与用户 延迟无关的后台任务;连接数高也可能都是长期 idle、但尚未达到瓶颈。
34.1.2 WAL、XID、复制槽、归档和长事务导致的保留
保留型不是“有人正在大量使用”
PostgreSQL 为恢复、复制和 MVCC 正确性保留历史。只要某个消费者仍声明“我可能需要 这里以前的内容”,系统就不能越过它回收:
| 被保留对象 | 常见保留者 | 关键边界 |
|---|---|---|
| 旧 tuple 版本 | 活跃快照、长事务、prepared transaction | backend_xmin、prepared XID |
| catalog tuple | logical replication slot | catalog_xmin |
| WAL segment | physical/logical slot、备库、备份 | restart_lsn |
| 待归档 WAL | archive command/repository 失败 | pg_stat_archiver 与归档队列 |
| 事务 ID 安全空间 | 未冻结表与被钉住的 xmin | relation/database age |
这里重要的是最老边界及其速度:
SELECT pid,
usename,
application_name,
state,
xact_start,
backend_xid,
backend_xmin
FROM pg_stat_activity
WHERE backend_xid IS NOT NULL
OR backend_xmin IS NOT NULL
ORDER BY xact_start NULLS LAST;
SELECT slot_name,
slot_type,
active,
xmin,
catalog_xmin,
restart_lsn,
wal_status,
safe_wal_size,
invalidation_reason
FROM pg_replication_slots
ORDER BY slot_name;
SELECT transaction, gid, prepared, owner, database
FROM pg_prepared_xacts
ORDER BY prepared;inactive slot 不等于废弃 slot。它可能对应暂时离线的副本、迁移、CDC 消费者或恢复流程; active slot 也不等于健康,消费者可能连着却不推进。必须把 slot 映射到服务 owner、 consumer、恢复承诺和最后成功时间。
先测增长,再谈释放
两个快照比一个快照更有意义:
t0: free bytes, current LSN, restart_lsn, archive failure count
t1: same fields after a known interval
retained bytes = current LSN - restart_lsn
growth rate = (retained_t1 - retained_t0) / elapsed
time to hard limit = usable headroom / positive growth rate若 max_slot_wal_keep_size=-1,replication slot 可以不受该参数上限地保留 WAL;即使配置
了有限值,相关状态也在 checkpoint 时才重新评估,不能把参数值误当作实时保险丝。
34.1.3 同样表现为“磁盘满”或“延迟高”,动作可以相反
症状相同,控制变量不同
| 症状 | 可能的 flow 根因 | 可能的 retention 根因 | 需要区分的证据 |
|---|---|---|---|
pg_wal 大 | 写流量突增、checkpoint 压力 | slot/归档/备份钉住 WAL | WAL 生成率与最老保留 LSN |
| 表膨胀 | 更新/删除量增长 | 长快照或 slot catalog_xmin | DML 率、vacuum 进度与 xmin |
| 磁盘延迟高 | 并发查询、temp、checkpoint | 被保留数据持续占满并触发写放大 | device queue、文件分类、增长率 |
| 连接失败 | 到达/重试超过连接预算 | 磁盘满后新事务无法写 WAL | pool queue、PG error、filesystem |
| CPU 高 | 执行/解析/自旋竞争 | recovery/cleanup 追赶保留积压 | backend type、wait、工作量变化 |
因此动作可以完全相反:
flow:
stop admission -> cancel exact work -> queue falls
retention:
preserve owner/lineage evidence -> repair consumer/archive
-> only then release an exact, authorized boundary把 retention 当 flow,取消再多普通查询也不会推进 restart_lsn。把 flow 当 retention,
忙着调查 slot 而不限制重试,服务可能先被连接和内存击穿。
同时发生怎么办
WAL slot 滞留期间又发生写入洪峰并不矛盾。事故记录应允许:
classification:
flow: confirmed
retention: confirmed
immediate_hard_failure: filesystem-full-in-18m
parallel_controls:
- reduce noncritical write admission
- preserve slot/archive evidence and contact owner
forbidden:
- delete pg_wal files
- drop unknown slot“只能选一个根因”是复盘分类,不是在线处理原则。
34.1.4 判型不清时先停止破坏性清理
unknown 是一种有效状态
下面任一项不明,就不能执行不可逆释放:
which path is failing?
which object is growing?
who owns the oldest xmin/restart_lsn?
is there a valid backup or replica copy?
what client work will be canceled?
what data or recovery capability can be lost?先收集最小证据包:
UTC + monotonic timestamp
user-facing probe and exact error class
pg_stat_activity grouped by app/state/wait
blocker tree and long/prepared transactions
pg_replication_slots and pg_stat_archiver
current/replay LSN and write rate
filesystem by mount/path plus inode usage
CPU/memory/pressure/device queue
recent config/deploy/failover/backup changes然后将路线写成机器与人都能审阅的三态判定:
FLOW
sufficient connection/queue/wait evidence
and no contradictory retention evidence for this action
RETENTION
exact owner boundary and retained quantity observed
and flow relief cannot release that boundary
STOP_AND_INVESTIGATE
both or neither predicate is sufficiently proved停止线包括:有人建议删 pg_wal、drop 未知 slot、终止未知大事务、在唯一副本上试验
危险参数、或以“磁盘快满”为由跳过 owner/backup 识别。此时应保护现场并回到第 31 章
的事故指挥框架。
本节交付物
进入具体止血前,至少写出:
symptom: exact user and resource observation
scope: service / database / node / time window
classification: FLOW | RETENTION | BOTH | UNKNOWN
supporting_evidence: [...]
contradicting_evidence: [...]
first_action: bounded and reversible
stop_condition: measurable
owner: named role没有这些字段,“先重启看看”不是 runbook。
返回本章目录 · 下一节:连接风暴与排队失控 · 查看全书目录 · 查看索引中心