PostgreSQL 17: 可配置 SLRU 缓存大小

John Doe 十月 14, 2025

在 PostgreSQL 中,除了共享缓冲区和 WAL 缓冲区外,还有一块用于事务状态的缓冲区。之前,这块缓冲区的大小是固定的。

image

特性提交日志

提升基于 SLRU 的子系统性能。

更具体地说,本次优化的核心是通过新增 GUC 配置参数使 SLRU 缓存大小支持自定义配置。这样一来,对于存在高并发场景、且有大量活跃事务(或大量多事务/子事务)的数据库实例,可通过增大缓存容量获得性能收益。为确保这一优化能高效发挥作用,我们还同步实施了两项额外变更:

  1. 将缓存划分为多个“bank”(借鉴 CPU 缓存的术语),像缓存页淘汰搜索这类算法仅会作用于某一个特定的 bank。这一设计解决了“在整个缓存池中线性搜索特定缓存页耗时过长”的问题,现在只需搜索目标缓存页所属的 bank,而单个bank的容量较小,搜索效率大幅提升。

  2. 调整 SLRU 各 bank 的加锁机制,为每个 bank 分配独立的 LWLock(轻量级锁)。这一改进能显著提升系统的并发扩展性。

我们特别注意了一种特殊场景:对于可能需要跨多个 bank 执行的算法,确保其在获取下一个 bank 的锁之前,会先释放前一个 bank 的锁。这种跨 bank 操作的情况并不常见,但clog.c中的组提交(group commit)功能需要针对性调整代码以适配该机制。此外,还添加了大量注释,以确保整体设计的合理性与可维护性。

新增的 GUC 参数名称,与此前在pg_stat_slru视图中引入的名称保持一致,便于用户关联监控与配置。

这些参数的默认值与各 SLRU 之前的默认缓存大小基本一致。其中,commit_tsclogsubtrans这三类 SLRU 的参数支持设为 0,当值为 0 时,缓存大小会根据shared_buffers(共享缓冲区)自动计算:用shared_buffers除以 512(即每 1GB shared_buffers对应 2MB 的 SLRU 缓存),且最大缓存容量限制为 8MB。(我们在slru.c中新增了SimpleLruAutotuneBuffers()函数来实现这一自动调整逻辑。)在此之前,clog的最大缓存容量仅为 1MB,因此对于shared_buffers超过 512MB 的数据库实例,此次优化会增加其 SLRU 缓存的总占用内存,这一权衡通常能带来显著的性能提升。

不过,其他类型的 SLRU(尤其是多事务相关的 SLRU)仍保持较小的默认缓存容量,且不支持将参数设为 0 以启用自动调整。目前基于shared_buffers计算缓存大小的逻辑未来可能会进一步调整,但这类调整的实现难度较低。

当初在引入这些新的 GUC 参数时,曾存在一些不同意见:有观点认为,理想的方案应是让系统能根据内存压力自动调整 SLRU 缓存(例如从shared_buffers中动态“借用”内存,使 SLRU 缓存可自然伸缩)。但要实现这一动态调整的方案,工程复杂度极高,且过去数年里相关工作几乎没有进展。鉴于 SLRU 缓存容量固定的问题已成为众多用户的核心痛点,我们最终选择了“支持手动配置”这一更务实的解决方案。

讨论:https://postgr.es/m/2BEC2B3F-9B61-4C1D-9FB5-5FAB0F05EF86@yandex-team.ru

什么是 SLRU 缓存?

在 PostgreSQL 中slru.c文件的 README 文档中,将 SLRU 定义为“用于可循环覆盖的永久性元数据的简单最近最少使用(LRU)缓冲机制”。

简单来说,PostgreSQL 中除了存储在表中的常规数据外,还需要维护某些特殊类型的元数据。例如,在 PostgreSQL 中使用 MultiXact、NOTIFY 功能,或需要长期保留的事务提交时间戳等信息,都属于这类元数据。

PostgreSQL 会将这些元数据存储在磁盘上,但同时也会在共享内存中对其进行缓存。因为这类元数据的访问频率通常很高,通过缓存可以避免频繁磁盘 IO 带来的性能损耗。

SLRU 缓存的作用

SLRU 缓存是 PostgreSQL 用于缓存轻量级、高频访问的元数据的关键组件,涵盖以下核心场景:

  • 事务状态(pg_xact):存储事务的提交 / 回滚状态,是事务隔离级别实现的基础;
  • 提交时间戳(pg_commit_ts):记录事务提交的时间戳,支持pg_xact_commit_timestamp()等函数查询;
  • 子事务(pg_subtrans):维护子事务与父事务的关联关系;
  • 组合事务(pg_multixact):处理行级锁的共享与排他关系(如 SELECT FOR UPDATE SHARE);
  • LISTEN/NOTIFY 消息(pg_notify):缓存实时通知消息,支撑消息订阅场景。

新增的服务器变量包括commit_timestamp_buffersmultixact_member_buffersmultixact_offset_buffersnotify_buffersserializable_bufferssubtransaction_bufferstransaction_buffers

其中,commit_timestamp_bufferstransaction_bufferssubtransaction_buffers会随shared_buffers(共享缓冲区)自动扩容。

利用 pg_stat_slru 监控 SLRU 缓存命中率

如果想判断当前系统是否存在 SLRU 缓存相关的性能问题,可以借助 PostgreSQL 的监控能力。自 PostgreSQL 13 起,系统中就新增了pg_stat_slru统计视图,专门用于展示这些内部缓存的运行状态。需要说明的是,在支持可配置之前,这些 SLRU 缓存的默认容量非常小,当时的缓存容量通常只有几兆字节。

若要排查当前系统是否受 SLRU 缓存问题影响,除了分析等待事件外,还可以查看pg_stat_slru视图中的blocks_hit(缓存命中块数)和blocks_read(磁盘读取块数)指标,由此判断 PostgreSQL 需要频繁从磁盘读取数据的频率。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/53c2a97a92665be6bd7d70bd62ae6158fe4db96e