跳至内容

34.4 CPU、内存、I/O 与 OOM

数据库资源彼此耦合。内存紧张会增加 reclaim 与 swap I/O;I/O 变慢会延长 query 和 transaction,占住更多连接与内存;连接排队又会触发超时重试,进一步增加 CPU。 事故中不能把每张主机图分开解释,而要寻找同一时间线上的因果方向。

34.4.1 饱和、排队、抖动与抢占

利用率不是完整答案

CPU 100% 可能仍有高吞吐且尾延迟可接受;CPU 40% 也可能因为单核热点、锁、自旋、 steal time 或 I/O 等待导致业务停滞。主机证据至少包括:

per-CPU user/system/iowait/steal
run queue and runnable tasks
context switches
memory pressure / reclaim / swap
per-device latency, queue depth, throughput and errors
cgroup/container limits and throttling
filesystem free bytes and inodes

再按 PostgreSQL backend type 与 wait event 对齐:

SELECT backend_type,
       wait_event_type,
       wait_event,
       count(*) AS processes
FROM pg_stat_activity
GROUP BY 1, 2, 3
ORDER BY processes DESC;

PostgreSQL 官方建议把统计视图与操作系统工具结合,因为数据库 I/O 统计不能区分数据 来自物理设备还是内核 page cache。数据库看到 read,也不等于磁盘实际发生同量读取。

饱和、排队和抖动

saturation
  resource has little service headroom

queueing
  work waits before resource service

jitter
  completion latency varies sharply over time

contention/preemption
  work loses CPU/lock/device service to other work

平均设备延迟 2 ms 可能掩盖 checkpoint 时 500 ms 尖峰;五分钟 CPU 平均值也会抹掉 每 30 秒同步到来的任务。保留原始采样粒度、时钟和分位数,不要只截一张平滑后的图。

先区分主机级还是数据库级

证据组合更可能的方向
host run queue 高,PG 多数无 waitCPU runnable 竞争
PG 大量 Lock,CPU 不高数据库锁序列化
device await/queue 高,PG 大量 IO存储服务能力不足
cgroup throttled,宿主机空闲容器/服务配额
swap/reclaim 高,连接与 query 同增内存承诺或并发过大
PG 平稳,其他进程占资源noisy neighbor/备份/扫描

不要在没确认 cgroup/虚拟化边界时用宿主机总容量推导数据库余量。

34.4.2 临时文件、并行、checkpoint 与后台维护

前台与后台会争同一设备

下面工作可能同时写盘:

sort/hash spill -> temp files
WAL writer / WAL sync
backend data writes
checkpointer flushing dirty buffers
autovacuum/vacuum
CREATE INDEX / REINDEX
base backup and archive
operating-system or storage maintenance

pg_stat_io 按 backend type、object 与 context 提供集群级 I/O 统计;pg_stat_database 的 temp 计数、日志中的 temporary file、pg_stat_checkpointerpg_stat_archiver 和 进度视图分别补充来源。统计是累计量,需要记录起点并取差值:

SELECT backend_type,
       object,
       context,
       reads,
       read_time,
       writes,
       write_time,
       extends,
       fsyncs,
       fsync_time
FROM pg_stat_io
ORDER BY backend_type, object, context;

列集合随 PostgreSQL 版本演进,生产脚本应绑定 major version 并做兼容检查。

临时文件是结果,不是单一根因

spill 可能来自:

  • work_mem 对该 sort/hash 太小;
  • 行数估计错误导致计划不合适;
  • 并发相同操作太多;
  • 查询本来就必须处理大量数据;
  • hash 操作按 hash_mem_multiplier 获得更高上限;
  • parallel workers 各自执行内存/临时工作。

直接把 work_mem 全局放大,可能把磁盘事故变成 OOM。优先修 query/统计、限制该工作 并发,必要时只对可控 role/session 做有界调整并验证。

checkpoint 峰值与追赶效应

checkpoint 需要把脏页推进到 durable storage。写流量突增、WAL 配置、恢复/重启后的 缓存重新填充和存储变慢都可能让 checkpoint 与前台 I/O 相互干扰。事故中记录:

checkpoint requested/timed
buffers written and write/sync duration
WAL generation rate
device write latency/queue
replica and archive progress

暂停 autovacuum 或 checkpoint 通常不是通用止血。autovacuum 还承担 XID freeze; 暂停后可能把短期 I/O 压力转成更危险的保留问题。只能对已识别对象、在明确时间窗与 回补计划下调整维护。

34.4.3 内存最坏并发、OOM killer 与进程重启

work_mem 不是每连接只分配一次

PostgreSQL 文档强调,work_mem 是一个 query operation(如 sort/hash)的基础上限; 一个复杂 query 可同时有多个 operation,多个 session 又可并发,parallel worker 也会 扩大总使用。粗略上界应按工作节点估算:

$$ M_{\text{query}} \approx \sum_{\text{sessions}} \sum_{\text{operations}} M_{\text{operation}} \sum_{\text{parallel workers}} M_{\text{worker}} $$

再加上:

shared_buffers and shared memory
backend base memory
maintenance_work_mem / autovacuum_work_mem
logical decoding and extension memory
kernel page cache
proxy, exporter, Patroni and other host processes
failure/recovery reserve

因此:

max_connections * work_mem

既不是准确实测,也不是足够保守的最坏值。它漏掉每 query 多个节点和并行,也忽略许多 非 work_mem 内存;反过来假设所有连接同时打满每个上限又可能极度悲观。容量测试要用 真实 workload envelope 和并发组合。

Linux OOM 不是数据库的流控机制

Linux overcommit 允许进程承诺超过物理内存的虚拟地址空间;真正耗尽时,OOM killer 可能选择某个进程。若 PostgreSQL child 被杀,postmaster 会把它当作异常退出,为保护 共享内存一致性,可能终止其他 server processes 并执行 crash recovery;若 postmaster 本身被杀,服务管理器行为又是另一条路径。

不要故意在共享/生产主机上“测一次 OOM”。安全实验应在有明确 cgroup/VM 边界的专用 环境里完成,并验证:

which cgroup/host reported OOM
which PID and backend_type was selected
whether postmaster remained
whether crash recovery occurred
client unknown outcomes
replica/archive/backup state after restart

本章正式实验明确不注入 OOM。

事故动作

内存压力下优先:

  1. 阻止新低价值工作和重试;
  2. 识别 exact 高内存 query/role/pool;
  3. 暂停可恢复 batch 与并行任务;
  4. 用 query cancel 逐步释放,而不是一次 terminate 全部;
  5. 保留管理连接与 OS 控制面;
  6. 观察 reclaim、swap、RSS、队列和业务成功率;
  7. 稳定后修正连接预算、query、并行与内存配置。

不要在事故中 drop OS cache:它既不能修复内存承诺,还会把后续读取推向存储,破坏 现场并制造新的 I/O 峰值。也不要把 swap 使用本身等同于故障;关键是持续 swap in/out、 memory pressure 与用户影响。

资源证据矩阵

cpu:
  utilization: ...
  run_queue: ...
  steal_or_throttle: ...
memory:
  available: ...
  pressure: ...
  swap_rate: ...
  oom_event: ...
io:
  device_latency_queue: ...
  pg_waits: ...
  checkpoint_temp_maintenance: ...
workload:
  admitted_running_waiting_rejected: ...
  root_queries_or_jobs: ...
decision:
  exact_control: ...
  stop_and_rollback: ...

单张 top 截图不够支撑数据库重启。


上一节:失控查询、锁与事务 · 返回本章目录 · 下一节:流量型止血动作 · 查看全书目录 · 查看索引中心

最后更新于