PostgreSQL内存占用过高如何排查和优化?

来源:Oracle教程作者:香港程序员头衔:程序员
导读:本期聚焦于香港程序员创作的《PostgreSQL内存占用过高如何排查和优化?》,敬请观看详情。PostgreSQL实例启动后内存只增不减,且峰值明显高于shared_buffers配置,这种问题通常不能只盯着缓存配置。共享缓冲区、进程私有内存、排序哈希操作、游标和预编译语句都可能成为隐藏大头。本文从内存结构入手,说明如何通过pg_stat_activity、pg_buffercache、pg_stat_database等系统视图定位内存来源,并给出shared_buffers、work_mem、max_connections、autovacuum等参数的调整思路。同时覆盖并行查询、临时文件和扩展组件可能引入的额外内存开销,帮助快速控制实例内存水位。建议结合RSS、PSS和内存上下文信息,避免单纯调小work_mem导致磁盘溢出,也避免盲目增大shared_buffers造成双缓存浪费。排查时还要关注长事务、闲置连接和过度使用临时文件的情况。

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

PostgreSQL内存占用过高如何排查和优化?

一、把内存账算清楚:共享内存与进程私有内存

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

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0920/59633.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。