PostgreSQL 行缓存与 shared_buffers 的比较

PostgreSQL 的 shared_buffers 保存数据库页。pg_local_cache 则另外按完整主键保存序列化后的整行载荷。页面已在内存中可以避免存储读取,但查询仍须从数据库元组生成结果。行缓存命中可在通过适用性与快照检查后直接返回已存载荷。

操作系统也可能缓存文件内容。因此,应以已预热的数据库作为基线。

SQL mget 读取路径 应用在 PostgreSQL 内调用 local_cache.mget。经过适用性与快照检查后,缓存命中返回已存整行载荷。未命中或绕过缓存时读取源表。两条路径均通过同一 PostgreSQL 连接返回行。 PostgreSQL 应用 local_cache.mget 适用性与快照 命中 未命中 / 绕过 已缓存
两条路径均通过同一 PostgreSQL 连接返回序列化整行。符合条件的源表读取可以填充缓存。

PostgreSQL 会缓存 SELECT 结果吗?

shared_buffers 缓存查询用到的页,而不是最终结果集。预备语句复用解析工作,也可能复用计划,但 PostgreSQL 仍然需要执行语句。pg_local_cache 通过显式 mget 调用添加整行缓存;它不会缓存任意 SELECT 结果或重写已有查询。Node.js 示例并列展示两种读取 API。

比较执行工作,而不仅是存储介质

读取方式 仍需执行的工作
基于热页的预备主键 SQL 协议处理、计划执行、行可见性检查和结果转换
可用的 SQL mget 缓存命中 协议处理、SQL 函数执行、键转换、缓存同步、快照检查及返回已存载荷
SQL mget 未命中或绕过缓存 函数检查加上源表查询;成功且符合条件的填充可以写入缓存

行缓存命中避免重复的源表执行和整行序列化。缓存检查与同步同样消耗 CPU,命中仍使用 PostgreSQL 连接与后端进程。此 SQL API 不会消除连接数上限或连接池排队。

必须计入的成本

即使源页已在内存中,缓存行仍占用额外共享内存。扩展还维护映射与失效状态。更新已关联的表会执行扩展触发器。当工作集超过容量时,看似优化读取的缓存,可能主要带来未命中与淘汰开销。

默认演示特意先用 1,024 个缓存槽位测试 128 行热数据,再首次遍历 4,096 行。基准测试指南解释这两种情况,并单独测量已关联表的写入成本。

何时保持现有应用不变

如果端到端延迟已经可接受、应用只需大行中的少量列,或主要工作是连接、范围查询与聚合,应保留已有查询。先将普通批量查询与当前逐键调用比较。批处理带来的收益不能证明缓存也有收益。

pg_local_cache 2.0 要求显式调用 mget、安装扩展并在启动时预加载。它拒绝启用 RLS、分区或继承的表。

行缓存还是外部缓存?

对于仍以 PostgreSQL 为权威来源的数据,此设计将失效处理放在数据库写入路径上,避免在应用中维护 cache-aside 协议。它不提供通用 Redis 语义。可选 RESP2 接口只有有限命令集,并具有独立安全模型。

PostgreSQL 行缓存不能替代基于 TTL 的应用状态、发布订阅或分布式协调。参阅技术约定事务示例

更新于 .