在PostgreSQL中,当数据库实例刚刚启动或者某张大表长时间未被访问时,相关数据页往往还停留在磁盘上,没有进入共享缓冲区。此时业务发起的第一次查询就会触发大量的物理读,响应时间可能比数据已在内存中时慢上数倍甚至数十倍。pg_prewarm是PostgreSQL官方贡献的一个扩展模块,它的作用就是把表、索引或者物化视图的页面主动加载到操作系统的文件系统缓存或数据库的共享缓冲区里,从而让后续的查询直接命中内存,避免冷启动带来的性能陡降。

pg_prewarm扩展的两种工作模式
pg_prewarm主要提供两种预热方式。第一种是手动预热,通过调用扩展提供的pg_prewarm函数,由数据库管理员或具备权限的用户显式指定要加载的关系名、加载模式以及页面范围。这种方式非常灵活,可以针对某张大表在批量报表运行前单独加热,也可以编写脚本在每日凌晨业务低峰期依次预热核心表。手动模式的核心在于控制权完全在用户手里,不会在数据库启动时产生额外的自动开销。
第二种是自动预热,依赖shared_preload_libraries中载入pg_prewarm,并配置pg_prewarm.autoprewarm参数为开启状态。数据库会在后台定期把当前缓冲区中的页面记录转储到磁盘文件,当下一次实例重启时,自动预热进程会读取该文件并尝试把记录的页面重新装入共享缓冲区。自动模式适合那些希望实例重启后尽快恢复到重启前缓存状态的场景,比如长期运行的在线服务,但它要求额外的磁盘写入与启动时的加载时间。
两种模式并不冲突,实际生产中常组合使用:自动预热负责基础恢复,手动预热负责针对特定业务高峰的补充加载。需要注意的是,自动预热的记录文件如果过大,重启时读取和加载都会变慢,因此应通过pg_prewarm.autoprewarm_interval合理控制转储频率。
手动预热函数的参数与使用示例
pg_prewarm函数的签名包含关系名、预热模式、缓冲池类型、起始块号和结束块号。预热模式分为prefetch、read和buffer三种。其中prefetch利用操作系统异步预读,不保证页面一定进入数据库共享缓冲区;read会同步读取页面到操作系统缓存;buffer则直接把页面读入PostgreSQL的共享缓冲区,对减少数据库层物理读最有效。缓冲池类型一般使用默认的main即可。
下面是一段手动预热整张订单表的示例,将全部页面装入共享缓冲区:
-- 先创建扩展(需超级用户)
CREATE EXTENSION IF NOT EXISTS pg_prewarm;
-- 将 public.orders 表全部装入共享缓冲区
SELECT pg_prewarm(
'public.orders', -- 关系名
'buffer', -- 预热模式:直接进共享缓冲区
'main', -- 缓冲池:默认主池
NULL, -- 起始块:从开头
NULL -- 结束块:到结尾
);
如果只希望预热前一千个数据块,可以把起始和结束块明确写出来,这在超大表部分预热时能显著缩短执行时间并降低对内存的冲击。此外,对索引的预热语法完全一致,只需把关系名换成索引名,就能让索引页同样常驻内存,对依赖索引扫描的查询帮助明显。
执行该函数要求调用者对该关系具有查询权限,且将页面读入共享缓冲区需要数据库有足够空闲缓冲槽,否则较旧的页面会被替换出去。因此在内存紧张的实例上,应当挑选真正热点的表来预热,而不是盲目加载全部库表。
自动预热机制与配置实践
要让PostgreSQL在重启后自动恢复缓冲区状态,首先必须在postgresql.conf里把pg_prewarm加入shared_preload_libraries,并置pg_prewarm.autoprewarm = on。数据库启动阶段会启动一个叫做autoprewarm master的辅助进程,它负责在实例关闭前由普通后台进程记录缓冲页,在启动后按记录重新加载。记录文件默认放在pg_prewarm自动管理的路径下,内容是所有需要预热的表空间、数据库、关系及块号列表。
以下是最简配置片段:
# postgresql.conf 相关配置 shared_preload_libraries = 'pg_prewarm' pg_prewarm.autoprewarm = on pg_prewarm.autoprewarm_interval = 300 # 每五分钟转储一次缓冲页记录
配置完成后重启实例,系统会自动在后台完成预热,无需人工干预。不过自动预热并非银弹:它只能恢复重启前已经在内存中的页面,如果某张表在重启前从未被访问过,自动预热也不会将其加入。同时,若实例共享缓冲区设置很小,而记录文件覆盖了远超缓冲区容量的页面,启动时会出现大量页面竞争,反而拖慢初始连接。
从运维角度看,建议把自动预热作为兜底手段,再配合监控脚本观察pg_buffercache视图中的命中率变化。当发现核心表未命中率偏高时,可临时用手动函数补充预热,从而在稳定性与灵活性之间取得平衡。
预热效果观察与常见误区
验证pg_prewarm是否生效,最直接的方法是查询pg_buffercache扩展提供的视图,确认目标关系的页面在bufferid中大量出现,同时观察pg_stat_database里的blks_read在预热后查询中不再快速增长。也可以对比预热前后同一条复杂查询的执行计划与耗时,通常首轮时间会有数量级下降。
一个常见误区是认为pg_prewarm能把数据永久钉在内存中。实际上共享缓冲区遵循淘汰算法,如果预热后实例持续面临其他大查询,之前预热的页面仍可能被换出。因此预热不是一劳永逸的操作,而是应当嵌入到运维节奏里,比如每天业务高峰前定时跑一次手动预热脚本。
另一个误区是忽略操作系统缓存层。使用prefetch或read模式时,页面可能只进入操作系统页缓存而未进数据库共享缓冲区,此时虽然减少了数据库自己管理的物理读,但仍有从OS缓存拷贝到共享缓冲的开销。在对延迟极度敏感的场景,应优先选择buffer模式并确认共享缓冲区尺寸充足。
pg_prewarmPostgreSQLbuffer_cache修改时间:2026-08-14 16:09:31