PostgreSQL的内存参数调优里,被问得最多的就是shared_buffers和work_mem这两个。它们一个管共享内存,一个管会话私有内存,作用机制完全不同,但很多初学者会把它们混为一谈,或者照搬网上的数值直接套用,结果数据库性能不升反降。要真正配好这两个参数,得先弄清楚它们各自在查询过程中扮演什么角色,再结合实际的硬件条件和业务负载来定值。

先弄懂两个参数的底层机制
shared_buffers是PostgreSQL启动时向操作系统申请的一块共享内存区域,用来缓存数据文件中的数据页。PostgreSQL默认的数据页大小是8KB,当你查询某张表时,系统会先检查这些页是否已经在shared_buffers里,命中就直接读内存,未命中才去磁盘读取。所以这块缓冲区的大小直接决定了缓存命中率,命中率低意味着大量物理IO,性能会明显下降。
work_mem的性质完全不同。它是每个会话在执行排序、哈希连接、哈希聚合这类操作时允许使用的内存上限。注意这里有个非常容易踩的坑:work_mem不是会话级的总量限制,而是单个查询节点级的限制。一条SQL里如果有三个排序节点和两个哈希连接,理论上最多可以同时消耗5倍的work_mem。更麻烦的是,每个并发连接都是独立计算的,100个连接同时跑复杂查询,内存压力会成倍放大。
当排序或哈希操作需要的内存超过work_mem时,PostgreSQL不会报错,而是把中间数据溢出到磁盘的临时文件里继续处理。这就是为什么work_mem设小了会看到查询变慢、pg_stat_activity里出现大量temp文件的原因。反过来设太大,一旦并发上来,内存被瞬间吃光,Linux的OOM killer就可能直接把PostgreSQL进程杀掉。
shared_buffers的推荐值与常见误区
关于shared_buffers,流传最广的说法是设成物理内存的25%。这个经验值对多数场景确实适用,比如一台16GB内存的专用数据库服务器,shared_buffers设4GB是一个不错的起点。但它不是铁律,原因在于PostgreSQL读写数据都要经过操作系统的文件系统缓存,如果shared_buffers占得太高,反而会让OS缓存被挤压,而OS缓存对顺序扫描和写入合并同样重要。
实际调优时可以参考这几个档位:2GB以下内存的小机器,shared_buffers不建议超过512MB,留更多空间给操作系统;4GB到16GB的机器取25%左右比较稳妥;超过32GB的大内存机器上,很多实践表明25%到40%之间都能取得不错效果,但需要配合压测验证。另外一个特殊情况是Windows平台,因为其内存管理机制不同,官方建议shared_buffers不要超过512MB到1GB。
判断shared_buffers是否够用,主要看两个指标:一是pg_stat_database视图中的blks_hit和blks_read比例,正常业务系统的缓存命中率应该稳定在99%以上;二是用EXPLAIN (ANALYZE, BUFFERS)查看查询的shared hit与read块数,如果read块数持续偏高,说明数据没被缓存住,可以考虑加内存或者调大shared_buffers。
-- 查看缓存命中率
SELECT datname,
round(blks_hit * 100.0 / nullif(blks_hit + blks_read, 0), 2) AS hit_ratio
FROM pg_stat_database
WHERE datname NOT LIKE 'template%';
work_mem怎么定值才不会翻车
work_mem的默认值只有4MB,对现代服务器来说偏保守。如果你的执行计划里频繁出现temp文件,或者sort节点显示Sort Method: external merge而不是quickSort,就说明排序内存不够用了。调整work_mem的目标,就是让绝大多数排序和哈希操作都能在内存中完成。
定值时有一个常用的估算思路:先用公式算出理论上可分配的内存,即(物理内存 - shared_buffers - 系统预留)除以最大并发活跃连接数,再除以单条查询的平均复杂节点数,得出的结果就是work_mem的安全上限。举个例子,16GB内存、shared_buffers占4GB、预留2GB给系统、最大活跃连接50个、平均每条查询2个内存密集节点,那么(16-4-2)*1024/50/2约等于102MB,保守取一半设50MB左右就比较安全。
除了全局设置,更推荐的做法是分级配置。全局的postgresql.conf里保持一个中等值,比如32MB到64MB,然后针对报表类大查询在会话级别动态调大:
-- 全局保持保守值,个别大查询临时调大 SET work_mem = '256MB'; SELECT ... -- 复杂报表查询 RESET work_mem; -- 或者只给特定角色设置 ALTER ROLE report_user SET work_mem = '256MB';
这种方式既保证了普通查询的内存充足,又避免了全局调大后并发场景下的内存失控。同时记得关注pg_stat_statements里的temp表写入量,用它来定位真正需要大内存的SQL,而不是盲目调参数。
组合调优的实战建议
把两个参数放在一起看,才能形成完整的内存规划。以一台16GB内存、跑混合负载(OLTP为主加少量报表)的服务器为例,一套比较合理的初始配置是:shared_buffers设4GB,effective_cache_size设12GB(这个参数不占内存,只是给优化器的成本估算参考),work_mem设64MB,maintenance_work_mem设512MB给建索引和VACUUM用。上线后持续观察命中率、temp文件和内存峰值,再逐步微调。
最后提醒两个常见误区:一是不要指望调参解决所有性能问题,缺少索引的查询调多少work_mem都救不回来,先用EXPLAIN ANALYZE定位真正的瓶颈;二是修改参数后要观察足够长的周期,最好覆盖业务高峰,因为缓存命中和内存压力都是随负载波动的,只看几分钟的数据很容易误判。调优本质上是个循环过程:观察、假设、调整、验证,参数值只是这个过程的产出,而不是一开始就能算出来的标准答案。
shared_bufferswork_memPostgreSQL调优修改时间:2026-09-05 12:50:41