pg_prewarm 是触发指定数据块读取的工具,非强制缓存预热命令;它仅尝试将页载入 shared_buffers,实际留驻受 LRU 管理和缓冲区压力影响。

pg_prewarm 是什么,为什么不能直接当“缓存预热命令”用
pg_prewarm 不是自动把整张表塞进 shared_buffers 的魔法命令。它只是触发一次对指定块(block)的读取操作,是否真能留在缓冲区里,取决于 PostgreSQL 的 LRU 管理逻辑和当时 shared_buffers 的压力状况。如果缓冲区已满、其他查询正在大量访问别的数据,刚预热的页可能立刻被踢出去。
所以它的作用更接近“主动触发物理读 + 尝试留驻”,而不是“强制锁定内存”。真实效果需要配合足够大的 shared_buffers 和较低的并发冲刷压力。
怎么调用 pg_prewarm 预热一张表的所有数据页
确保扩展已启用:
CREATE EXTENSION IF NOT EXISTS pg_prewarm;
然后执行预热(以表 orders 为例):
SELECT pg_prewarm('orders', 'buffer', 'main');
-
'orders':目标表名(支持 schema.qualified 形式,如'public.orders') -
'buffer':表示预热到shared_buffers;也可用'prefetch'(触发 OS 层预读,不进 shared_buffers)或'read'(同步读取但不缓存) -
'main':表示主数据文件;还可选'fsm'(空闲空间映射)、'vm'(可见性映射)等辅助文件
注意:pg_prewarm() 返回预热的块数,可用来确认是否覆盖了全表(对比 pg_total_relation_size('orders') / current_setting('block_size')::int)。
预热失败或效果差的常见原因
以下情况会导致预热“看似成功”但实际没留在 shared_buffers 中:
- 执行时
shared_buffers已被占满,新读入页立即被替换 - 表太大,预热过程持续时间长,中间有其他高 I/O 查询干扰缓冲区
- 使用了
'prefetch'模式,误以为进了 shared_buffers(其实只进了 OS page cache) - 表上有大对象(TOAST 表),
'main'不会自动预热 TOAST 内容,需单独调用pg_prewarm('orders_toast') - 数据库启用了
effective_io_concurrency > 0且存储响应慢,'prefetch'可能超时失败,但'buffer'模式不受影响
生产环境预热建议与注意事项
预热不是一劳永逸的操作,尤其在频繁重启或缓冲区被清空后(如 pg_reload_conf() 不影响,但 pg_terminate_backend() 大量触发可能导致 LRU 波动):
- 避免在业务高峰执行;最好在低峰期或重启后立即运行
- 对分区表,需逐个分区调用
pg_prewarm(),父表名不会自动递归 - 想验证是否真进缓存?查
pg_buffercache视图(需pg_buffercache扩展):SELECT count(*) FROM pg_buffercache WHERE relfilenode = (SELECT relfilenode FROM pg_class WHERE relname = 'orders');
- 不要依赖单次预热长期有效;若业务模式稳定,可考虑写脚本在每天启动后自动运行
真正难的不是调用函数,而是判断“哪些表值得预热”以及“什么时候预热才不被冲掉”。这需要结合 pg_stat_bgwriter 的 buffers_clean、maxwritten_clean,还有 pg_stat_database 的 blks_read/blks_hit 比率一起看。










