1. 缓存基础概念:PostgreSQL 共享缓冲区、操作系统缓存、WAL 缓冲区的作用与区别
在 PostgreSQL 查询性能的“回归冒烟”测试中,缓存是决定查询响应速度的第一道关卡。理解三种核心缓存——共享缓冲区、操作系统缓存、WAL 缓冲区——的作用与区别,是后续索引优化和性能测试的基石。下图展示了数据从磁盘到查询结果的完整缓存路径。
核心要点: 共享缓冲区是 PostgreSQL 自身管理的数据页缓存;操作系统缓存是内核层面的文件系统页缓存;WAL 缓冲区是事务日志的临时写入区域。三者共同决定了 I/O 行为与事务性能。
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 缓存层级与查询路径
一次典型的查询请求,数据页的访问路径如下:
- 客户端发起查询 → PostgreSQL 解析器生成执行计划。
- 访问共享缓冲区:如果目标数据页已在共享缓冲区中,直接返回(缓存命中)。
- 访问操作系统缓存:如果共享缓冲区未命中,PostgreSQL 发起
read()系统调用,内核检查 OS 页缓存。 - 磁盘 I/O:如果 OS 缓存也未命中,内核发起磁盘 I/O 读取数据块,同时填充 OS 缓存。
- 填充共享缓冲区:从 OS 缓存读取的数据页被加载到共享缓冲区,供后续查询使用。
冒烟测试关键指标: 在索引优化前后,对比
pg_stat_user_tables 中的 heap_blks_hit 与 heap_blks_read 比值。命中率从 90% 提升到 99% 以上,通常意味着索引优化有效减少了随机 I/O。