PostgreSQL 的内存占用问题通常表现为进程 RSS 持续上升、操作系统缓存被挤压、业务高峰期出现 OOM 或者查询变慢。需要先明确一点:PostgreSQL 并不是把所有内存都放进 shared_buffers,进程启动后每个后端都有独立的私有内存,另外还有操作系统页缓存参与数据读写。因此排查时不能只看某一个参数,而要结合共享内存、进程私有内存、连接规模、临时文件和外部扩展一起分析。

一、把内存账算清楚:共享内存与进程私有内存
PostgreSQL 的内存可以粗略分成三大块:共享内存、后端进程私有内存和操作系统页缓存。共享内存由 postmaster 启动时分配,所有后端进程共享,主要受 shared_buffers、wal_buffers、锁表、预备事务等参数影响。其中 shared_buffers 是最常见的配置项,它决定数据库自身缓存数据页的大小,但并不是越大越好。很多场景下,如果 shared_buffers 设置得过高,反而会降低操作系统页缓存的效率,形成双重缓存浪费。
后端进程私有内存则包含很多容易忽略的部分,比如执行排序和哈希聚合时使用的 work_mem、VACUUM 和 CREATE INDEX 使用的 maintenance_work_mem、临时表使用的 temp_buffers、游标和预编译语句持有的结果集等。需要注意的是,一条 SQL 里如果有多个排序或哈希操作,每个操作都可能单独申请 work_mem,因此理论上的峰值内存会远大于单个 work_mem 的值。这也是为什么有时单个查询就能把服务器内存打满。
操作系统页缓存不直接由 PostgreSQL 管理,但它会缓存数据文件和 WAL 文件。如果数据库实例内存紧张,可以先查看操作系统的 free -m 输出,区分 used、buff/cache 和 available。很多情况下 available 并不低,但 PostgreSQL 进程本身占用的 RSS 较高,这时就需要继续排查后端进程的私有内存而不是继续调 shared_buffers。
下面这段 SQL 可以查看当前实例中几个关键内存参数的实际值和单位:
SHOW shared_buffers;
SHOW work_mem;
SHOW maintenance_work_mem;
SHOW max_connections;
SELECT name, setting, unit, context
FROM pg_settings
WHERE name IN ('shared_buffers','work_mem','maintenance_work_mem','temp_buffers','max_connections');
二、利用系统视图定位内存占用来源
当内存占用偏高时,第一步通常是查看当前连接和活跃查询。pg_stat_activity 能显示每个后端的 PID、用户、状态、等待事件、事务开始时间和当前 SQL。长事务和闲置连接不仅会占用连接槽位,还可能导致快照一直无法清理,进而拖累 VACUUM 和内存回收。可以先按事务持续时间排序,找出长时间未结束的会话:
SELECT pid, usename, state, wait_event_type, wait_event,
now() - xact_start AS xact_duration, query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY xact_start NULLS LAST
LIMIT 20;
如果存在大量 idle in transaction 状态的连接,需要重点检查应用程序是否显式开启事务后没有及时提交或回滚。这类连接会让后端继续持有私有内存和锁资源,同时阻止 VACUUM 回收死元组。对于空闲连接,可以通过以下语句统计各状态的连接数量:
SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY count(*) DESC;
共享缓冲区的具体使用情况可以借助 pg_buffercache 扩展分析。它能够列出当前 shared_buffers 中缓存了哪些关系的数据页,帮助判断是否存在某张表或索引长期占据大量缓冲区。使用前需要在数据库中执行 CREATE EXTENSION pg_buffercache,然后查询占用最多的关系:
CREATE EXTENSION IF NOT EXISTS pg_buffercache; SELECT c.relname, count(*) AS buffers FROM pg_buffercache b JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid) GROUP BY c.relname ORDER BY buffers DESC LIMIT 10;
另一个有用的视图是 pg_stat_database,它可以反映临时文件的使用量。临时文件通常由排序、哈希、物化等操作在 work_mem 不足时写入磁盘产生,虽然不直接占用内存,但频繁产生临时文件说明 work_mem 设置偏小,反过来如果为了避免临时文件盲目调大 work_mem,又会造成内存过高。查询当前数据库的临时文件统计:
SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_size FROM pg_stat_database WHERE datname = current_database();
对于 PostgreSQL 14 及以上版本,还可以通过 pg_backend_memory_contexts 查看当前后端内存上下文的详细分配。这个视图把内存划分为 CacheMemoryContext、ExecutorState、TopTransactionContext 等区域,可以快速发现是哪一部分逻辑占用了异常内存。尤其在排查复杂查询或扩展组件时很有帮助。
三、关键参数调整与优化实践
shared_buffers 的调整需要结合物理内存、实例规模和业务负载。一般建议设置为物理内存的 25% 到 40%,但超过 8GB 后收益通常递减。对于以写为主或大量使用操作系统缓存的场景,可以适当降低 shared_buffers,把更多内存留给页缓存。对于只读分析型负载,则可以适当提高,减少磁盘读取。
work_mem 是最容易被误调的参数。它控制内部排序和哈希表的内存上限,单位为 kB 或 MB,默认值通常为 4MB。如果业务中有大量聚合、排序、DISTINCT 操作,可以将其调到 16MB 或 32MB,但不要设置过大。因为 work_mem 是每个操作可用的内存,当一条 SQL 包含多路排序且连接数很高时,单个查询的峰值内存可能成倍增长。建议先在测试环境用 EXPLAIN ANALYZE 观察实际使用情况,再逐步调整。
max_connections 也会放大内存问题。每个后端连接即使空闲也会占用一定的基础内存,如果应用直接使用数据库长连接且没有连接池,几百个连接叠加起来的内存非常可观。优先考虑在应用侧引入 PgBouncer、Pgpool-II 或应用框架自带的连接池,降低数据库直连数量。数据库本身的 max_connections 不要设置得远超实际需求,通常配合连接池后控制在 100 到 300 即可。
维护类操作的内存由 maintenance_work_mem 控制,它影响 VACUUM、CREATE INDEX、ALTER TABLE 等任务。如果数据库存在大表维护需求,可以将其设置为 1GB 或更高,但要注意该参数只对当前会话生效,多个维护任务同时运行时内存会叠加。autovacuum 工作进程使用 autovacuum_work_mem,默认从 maintenance_work_mem 继承。以下示例展示如何通过 ALTER SYSTEM 调整关键参数:
ALTER SYSTEM SET shared_buffers = '4GB'; ALTER SYSTEM SET work_mem = '16MB'; ALTER SYSTEM SET maintenance_work_mem = '1GB'; ALTER SYSTEM SET max_connections = 200; SELECT pg_reload_conf();
四、内存泄漏与外部扩展排查
如果参数已经合理,但内存仍持续增长且不回落,就要考虑内存泄漏。PostgreSQL 内核本身较少出现内存泄漏,但第三方扩展、PL/pgSQL 函数、C 函数、FDW 外部表封装器或某些版本的特殊执行路径可能造成问题。可以观察后端进程的 RSS 是否会随着时间单调增长,并结合 pg_backend_memory_contexts 查看内存上下文分配。
在 PostgreSQL 14 及以上版本中,可以查询当前会话的内存上下文统计,找出占用最大的区域:
SELECT name, total_bytes, used_bytes, free_bytes FROM pg_backend_memory_contexts WHERE used_bytes > 1048576 ORDER BY used_bytes DESC LIMIT 20;
如果怀疑某个扩展,可以在独立会话中重复执行同一操作,观察 used_bytes 是否会不断上升。也可以利用 EXPLAIN (ANALYZE, BUFFERS) 查看执行计划中的内存消耗和缓冲区命中情况。对于复杂 SQL,计划中的 Sort Method 和 Hash Information 会显示使用了多少内存,如果显示 external merge 或 Disk,说明 work_mem 不足;如果内存使用量远高于预期,则需要优化查询或检查统计信息。
内存占用过高的排查不是一次性工作。建议建立基线监控,记录 PostgreSQL 进程的 RSS、PSS、连接数、临时文件量和锁等待情况。在业务变更或参数调整后持续观察,才能找到最适合当前环境的配置组合。
PostgreSQL内存shared_buffers内存优化修改时间:2026-09-20 11:50:23