1. 适用场景

在数据库日常运维与大型变更中,最让 DBA 和基础架构团队头疼的问题之一就是冷启动(Cold Cache)引发的性能骤降与 I/O 尖刺

核心适用场景

  1. 生产主从切换(Failover / Switchover)
  • 计划内的物理主从切换或逻辑备库接管生产流量前,目标节点内存处于完全冷态。
  1. 硬件扩容 / 数据库版本升级
  • 业务迁移到更高配机器或新集群时,需要目标机在承接前端高并发的第一秒就具备与老机器完全一致的缓存命中率。
  1. 数据库实例重启维护(Rolling Restart)
  • 解决参数调优或宿主机维护重启后,前几分钟内内存命中率暴跌、磁盘 I/O 被打满、前端连接池爆满甚至雪崩的问题。

2. 技术原理:PostgreSQL 的“双层缓存”架构与预热机制

PostgreSQL 的读写架构并非只依赖自身的数据库内存,而是深度依赖 数据库共享缓冲区(shared_buffers) + 操作系统页缓存(OS Page Cache) 的双层缓存体系:

+-------------------------------------------------------------------------+
|                              客户端查询请求                              |
+------------------------------------+------------------------------------+
                                     |
                                     v
+-------------------------------------------------------------------------+
|  Layer 1: PostgreSQL shared_buffers (如 32GB)                           |
|  - 存放最热点的 8KB 数据块                                                |
|  - 命中则产生 0 系统调用,极速返回 (微秒级)                                 |
+------------------------------------+------------------------------------+
                                     | (未命中: 发起 read() 系统调用)
                                     v
+-------------------------------------------------------------------------+
|  Layer 2: Linux OS Page Cache (如 30GB+)                                |
|  - 存放非 shared_buffers 的温热数据文件页                                 |
|  - 命中则由内核直接拷贝内存,产生的物理磁盘 IOPS = 0                       |
+------------------------------------+------------------------------------+
                                     | (未命中: 发起真实底层物理磁盘 I/O)
                                     v
+-------------------------------------------------------------------------+
|  Layer 3: 物理存储设备 (NVMe SSD / 云盘 EBS / SAN)                        |
|  - 产生真实物理 IOPS 与读延迟 (毫秒级)                                    |
+-------------------------------------------------------------------------+

  • shared_buffers 预热:通过 pg_prewarm 扩展的 autoprewarm 机制,将源库当前的内存块元信息(Database OIDRelation FileNodeFork NumberBlock Number)序列化写入快照文件(autoprewarm.blocks),在目标库上一键回灌。
  • OS Page Cache 预热:通过 Linux 内核预读工具(如 ddfadvisevmtouch)将剩余几十 GB 的大表及核心索引物理文件载入操作系统文件缓存,彻底消除二次未命中的物理 I/O 穿透。

3. 实战操作步骤

步骤 1:在源库(当前生产库)导出 shared_buffers 内存快照

确认并创建 pg_prewarm 扩展,执行即时转储指令:

-- 1. 登录当前生产库
\c prod_db

-- 2. 确保扩展已安装
CREATE EXTENSION IF NOT EXISTS pg_prewarm;

-- 3. 将当前 shared_buffers 内部的所有热块转储至磁盘文件
SELECT autoprewarm_dump_now();

执行结果示例:

 autoprewarm_dump_now 
----------------------
              3932160
(1 row)

返回值 3932160 代表成功导出了约 393 万个 8KB 数据块(对应约 30GB 的缓存状态)。该文件默认生成在源库的数据目录 $PGDATA/autoprewarm.blocks


步骤 2:将快照文件传输至目标库

在操作系统层,通过 scp 或网络同步工具将文件复制到目标库对应的 $PGDATA 目录下:

# 1. 在源库执行传输(请根据实际环境替换数据目录路径与目标 IP)
scp /var/lib/pgsql/14/data/autoprewarm.blocks postgres@target_node_ip:/var/lib/pgsql/14/data/autoprewarm.blocks

# 2. 登录目标库服务器,确保文件权限与归属正确
chown postgres:postgres /var/lib/pgsql/14/data/autoprewarm.blocks
chmod 600 /var/lib/pgsql/14/data/autoprewarm.blocks


步骤 3:在目标库完成 Layer 1(shared_buffers)预热

在目标库安装扩展,并启动预热加载:

方式 A:无重启在线手动触发加载(推荐在切流前 15 分钟执行)
-- 1. 登录目标库
\c prod_db

-- 2. 确保扩展已启用
CREATE EXTENSION IF NOT EXISTS pg_prewarm;

-- 3. 触发全量回灌进 shared_buffers
SELECT pg_prewarm(
    c.oid,
    'buffer',
    'main',
    NULL,
    NULL
) 
FROM (
    SELECT DISTINCT relfilenode 
    FROM pg_buffercache 
    WHERE reldatabase = (SELECT oid FROM pg_database WHERE datname = 'prod_db')
) sub
JOIN pg_class c ON c.relfilenode = sub.relfilenode;

方式 B:配置后台进程随库自动加载(适合停机升级/重启维护)

在目标库的 postgresql.conf 中追加配置:

shared_preload_libraries = 'pg_prewarm'
pg_prewarm.autoprewarm = true
pg_prewarm.autoprewarm_interval = 300s

重启数据库后,后台守护进程 autoprewarm worker 会自动读取 $PGDATA/autoprewarm.blocks 并完成数据升温。


步骤 4:在目标库完成 Layer 2(Linux OS Page Cache)预热

为了防止 shared_buffers 未命中的大范围查询直接穿透到底层磁盘,需要在操作系统层面将核心表及索引文件直接读入 Linux Page Cache。

1. 查询核心热表及其索引的底层物理文件路径

登录目标库执行以下 SQL,获取核心大表及其主键/核心索引的文件路径:

SELECT 
    c.relname,
    c.relkind,
    pg_relation_filepath(c.oid) AS filepath,
    pg_size_pretty(pg_relation_size(c.oid)) AS size
FROM pg_class c
WHERE c.relname IN ('t_user_resource', 't_resource_active', 'idx_resource_active_pk', 't_order_flow')
ORDER BY pg_relation_size(c.oid) DESC;

输出示例:

      relname      | relkind |        filepath        |  size   
-------------------+---------+------------------------+---------
 t_resource_active | r       | base/16384/24589       | 24 GB
 idx_resource_pk   | i       | base/16384/24595       | 357 MB

2. 使用 Linux 工具在系统层将文件预读进内存

在目标库宿主机终端(使用 postgresroot 权限),通过以下任一方式执行物理预读:

  • 方法一:原生流式预读(零门槛,适用大多数场景)
# 进入 PG 数据目录
cd /var/lib/pgsql/14/data

# 读取核心表及所有分段文件 (24589, 24589.1, 24589.2...) 至空设备,填满 OS 缓存
cat base/16384/24589* > /dev/null
cat base/16384/24595* > /dev/null

  • 方法二:使用 vmtouch 锁定与加载(精准控制)
    如果系统安装了 vmtouch 工具,可以更直观地加载并查看 OS 缓存驻留状态:
# 预加载核心文件并显示加载进 OS 内存的比例
vmtouch -t -v /var/lib/pgsql/14/data/base/16384/24589*


4. 预热效果验证与监控

1. 验证 shared_buffers 命中情况

在目标库运行以下 SQL,检查各热度等级的分布:

SELECT 
    usagecount,
    count(*) AS pages,
    pg_size_pretty(count(*) * 8192) AS total_size,
    ROUND(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name='shared_buffers')::numeric, 2) AS pct_of_shared_buffers
FROM pg_buffercache
GROUP BY usagecount
ORDER BY usagecount;

2. 验证 OS Page Cache 驻留情况

在 Linux 终端执行 free -mtop

free -m

指标确认:

  • buff/cache 项应明显增加(例如从最初的几百 MB 提升至 25GB~40GB),说明文件已完整驻留在系统内存中。

3. 执行计划压测验证

切流前抽取线上典型高频 SQL 执行 EXPLAIN (ANALYZE, BUFFERS)

Index Only Scan using idx_resource_active_pk on t_resource_active  (cost=0.43..2.66 rows=1 width=16) (actual time=0.028..0.028 rows=1 loops=1)
  Buffers: shared hit=4 read=0
Execution Time: 0.045 ms

  • shared read=0:表示完全未发起物理磁盘读取;
  • 耗时维持在毫秒以内,证明目标库已具备“全热机”接管能力。

5. 生产环境避坑指南

  1. 同构架构最佳
  • autoprewarm.blocks 文件依赖于底层的 Relation FileNode。在物理流复制(Streaming Replication)主备机之间、或同版本直接复制的环境下具有最佳兼容性。
  1. 逻辑复制环境下的差异
  • 若目标库是通过逻辑复制(Logical Replication)初始化的,因为底层表的 FileNode 可能存在差异,若快照加载部分失效,可直接运行 SELECT pg_prewarm('核心表名', 'buffer'); 进行显式预热,并结合操作系统层 cat / dd 预读底层文件。
  1. OS 内核参数配合调优
    确保 Linux 内核不会过早回收文件缓存或触发 Swap:
# /etc/sysctl.conf
vm.vfs_cache_pressure = 50  # 降低对文件缓存的回收倾向
vm.swappiness = 1           # 极大降低 Swap 交换

  1. 预热时机控制
  • 建议在正式切流前 15~30 分钟完成快照导出与预热,避免过早执行导致缓存再次被后台任务换出,或过晚执行造成预热 I/O 与切流流量并发争用。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐