1. 缓存基础概念:PostgreSQL 共享缓冲区、操作系统缓存、WAL 缓冲区的作用与区别

在 PostgreSQL 查询性能的“回归冒烟”测试中,缓存是决定查询响应速度的第一道关卡。理解三种核心缓存——共享缓冲区、操作系统缓存、WAL 缓冲区——的作用与区别,是后续索引优化和性能测试的基石。下图展示了数据从磁盘到查询结果的完整缓存路径。

核心要点: 共享缓冲区是 PostgreSQL 自身管理的数据页缓存;操作系统缓存是内核层面的文件系统页缓存;WAL 缓冲区是事务日志的临时写入区域。三者共同决定了 I/O 行为与事务性能。
PostgreSQL 三层缓存架构图 PostgreSQL 三层缓存架构 客户端查询 共享缓冲区 (Shared Buffers) 默认 128MB · 存储数据页 · LRU 淘汰 直接减少磁盘读取 · 脏页延迟写入 操作系统缓存 (OS Page Cache) 内核管理 · 文件系统级缓存 · 预读机制 对 PostgreSQL 透明 · 可缓存大量数据 磁盘 (Disk) SSD / HDD WAL 缓冲区 默认 16MB 事务日志暂存 WAL 写入 检查点 缓存命中路径 客户端 → 共享缓冲区 (命中) 未命中 → OS 缓存 → 磁盘

1.1 共享缓冲区(Shared Buffers)

共享缓冲区是 PostgreSQL 进程自身管理的数据页缓存区域,位于数据库共享内存中。所有后端进程(backend process)通过它读取和修改数据页。

  • 存储内容: 表数据页、索引数据页、空闲空间映射(FSM)、可见性映射(VM)等。
  • 淘汰策略: 时钟扫描(clock sweep)近似 LRU 算法,兼顾脏页写入频率。
  • 默认大小: 128MB(实际生产环境通常设置为物理内存的 15%~25%)。
  • 关键参数: shared_buffers、bgwriter_delay、bgwriter_lru_maxpages。
  • 性能影响: 命中共享缓冲区可避免系统调用和磁盘 I/O,是查询性能的第一道防线。
冒烟测试提示: 在索引优化前后,使用 pg_buffercache 扩展观察共享缓冲区中特定表和索引的缓存命中率,是判断索引是否被有效使用的直接手段。

1.2 操作系统缓存(OS Page Cache)

操作系统缓存是内核管理的文件系统页缓存。PostgreSQL 通过 open()/read()/write() 系统调用读写数据文件时,内核会自动将文件内容缓存在物理内存中。

  • 对 PostgreSQL 透明: 数据库无法直接控制 OS 缓存的淘汰策略,但可以通过 posix_fadvise() 等接口给出提示。
  • 容量优势: 通常远大于 shared_buffers,可缓存整个数据库的热数据。
  • 预读机制: 内核会按顺序预读后续数据页,对全表扫描有利。
  • 双缓存问题: 如果 shared_buffers 过大,可能导致数据在共享缓冲区和 OS 缓存中各存一份,浪费内存。
  • 清空方法: echo 3 > /proc/sys/vm/drop_caches(仅用于测试环境)。
冒烟测试陷阱: 首次查询后,OS 缓存可能已经缓存了数据文件。第二次查询即使共享缓冲区未命中,也可能从 OS 缓存直接返回,导致“假性性能提升”。测试前必须清空 OS 缓存或使用 pg_prewarm 控制预热状态。

1.3 WAL 缓冲区(WAL Buffer)

WAL(Write-Ahead Logging)缓冲区是用于暂存事务日志记录的内存区域。所有数据修改(INSERT/UPDATE/DELETE)在写入数据页之前,必须先写入 WAL 缓冲区,再刷入磁盘 WAL 段文件。

  • 存储内容: 事务的 REDO 日志记录,包括页面偏移、旧值、新值等。
  • 默认大小: 16MB(生产环境通常设置为 64MB~256MB)。
  • 刷盘时机: 事务提交(commit)、WAL 缓冲区满、检查点、后台写进程触发。
  • 关键参数: wal_buffers、wal_writer_delay、commit_delay。
  • 性能影响: 过小的 WAL 缓冲区会导致频繁刷盘,增加事务提交延迟;过大会浪费内存且无额外收益。

1.4 三者对比总结

对比维度 共享缓冲区 操作系统缓存 WAL 缓冲区
管理主体 PostgreSQL 进程 操作系统内核 PostgreSQL 进程
存储内容 数据页、索引页、FSM、VM 文件系统数据块 事务 REDO 日志
默认大小 128MB 动态(取决于可用内存) 16MB
淘汰策略 时钟扫描(近似 LRU) 内核 LRU + 预读 顺序写入 + 刷盘后释放
对查询性能影响 直接决定数据页命中率 间接影响,二次查询加速 影响写入性能,不影响读
清空方式 重启数据库 / pg_buffercache 重置 drop_caches / 重启 重启数据库 / 检查点
监控视图 pg_buffercache /proc/meminfo / sar -r pg_stat_wal

1.5 缓存层级与查询路径

一次典型的查询请求,数据页的访问路径如下:

  1. 客户端发起查询 → PostgreSQL 解析器生成执行计划。
  2. 访问共享缓冲区:如果目标数据页已在共享缓冲区中,直接返回(缓存命中)。
  3. 访问操作系统缓存:如果共享缓冲区未命中,PostgreSQL 发起 read() 系统调用,内核检查 OS 页缓存。
  4. 磁盘 I/O:如果 OS 缓存也未命中,内核发起磁盘 I/O 读取数据块,同时填充 OS 缓存。
  5. 填充共享缓冲区:从 OS 缓存读取的数据页被加载到共享缓冲区,供后续查询使用。
冒烟测试关键指标: 在索引优化前后,对比 pg_stat_user_tables 中的 heap_blks_hit 与 heap_blks_read 比值。命中率从 90% 提升到 99% 以上,通常意味着索引优化有效减少了随机 I/O。