由 John Doe 十月 14, 2025
在 PostgreSQL 中,除了共享缓冲区和 WAL 缓冲区外,还有一块用于事务状态的缓冲区。之前,这块缓冲区的大小是固定的。
特性提交日志
提升基于 SLRU 的子系统性能。
更具体地说,本次优化的核心是通过新增 GUC 配置参数使 SLRU 缓存大小支持自定义配置。这样一来,对于存在高并发场景、且有大量活跃事务(或大量多事务/子事务)的数据库实例,可通过增大缓存容量获得性能收益。为确保这一优化能高效发挥作用,我们还同步实施了两项额外变更:
-
将缓存划分为多个“bank”(借鉴 CPU 缓存的术语),像缓存页淘汰搜索这类算法仅会作用于某一个特定的 bank。这一设计解决了“在整个缓存池中线性搜索特定缓存页耗时过长”的问题,现在只需搜索目标缓存页所属的 bank,而单个bank的容量较小,搜索效率大幅提升。
-
调整 SLRU 各 bank 的加锁机制,为每个 bank 分配独立的 LWLock(轻量级锁)。这一改进能显著提升系统的并发扩展性。
我们特别注意了一种特殊场景:对于可能需要跨多个 bank 执行的算法,确保其在获取下一个 bank 的锁之前,会先释放前一个 bank 的锁。这种跨 bank 操作的情况并不常见,但clog.c
中的组提交(group commit)功能需要针对性调整代码以适配该机制。此外,还添加了大量注释,以确保整体设计的合理性与可维护性。
新增的 GUC 参数名称,与此前在pg_stat_slru
视图中引入的名称保持一致,便于用户关联监控与配置。
这些参数的默认值与各 SLRU 之前的默认缓存大小基本一致。其中,commit_ts
、clog
和subtrans
这三类 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_buffers
、multixact_member_buffers
、multixact_offset_buffers
、notify_buffers
、serializable_buffers
、subtransaction_buffers
和transaction_buffers
。
其中,commit_timestamp_buffers
、transaction_buffers
和subtransaction_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