PostgreSQL 17: pg_buffercache_evict 函数

John Doe 五月 23, 2025

你需要在 EXPLAIN 查询的时候,清空缓冲区中的缓存吗?现在,PostgreSQL 提供了清空缓存的函数。

在山坡漫步的大象

特性提交日志

为测试添加 pg_buffercache_evict() 函数。

在测试缓冲池逻辑时,若能够逐出任意数据块会非常有用。该函数可在针对 pg_buffercache 视图的 SQL 查询中使用,以设置一段缓冲的状态。当然,缓冲的映射可能会并发更改,因此您逐出的数据块可能并非预期的目标块,且其他会话可能随时将其重新加载。对于开发测试场景的用途而言,这种预期是可以接受的;此外,若采用更复杂的锁定方案来提供更强的一致性保证,反而可能在实际测试工作中降低灵活性。该函数仅限超级用户使用。

讨论:https://postgr.es/m/CALfch19pW48ZwWzUoRSpsaV9hqt0UPyaBPC4bOZ4W+c7FF566A@mail.gmail.com

示例

在之前的 PostgreSQL 版本中,除了重启实例外,没有其他方法可以清除缓冲区缓存。在 Oracle 中,你可以使用 alter system flush buffer cache 命令清除缓冲区缓存,但在 PostgreSQL 中不行。这种情况现在得到了改变。当然,在生产环境中,你通常不会想清除缓冲区缓存,但这在诊断或调试方面非常方便。这也是为什么它被设计为开发者功能的原因。

为了访问 pg_buffercache_evict 函数,您需要安装 pg_buffercache 扩展,因为该函数会影响 pg_buffercache 视图的结果:

postgres=# create extension pg_buffercache;

postgres=# \dx
                      List of installed extensions
      Name      | Version |   Schema   |           Description
----------------+---------+------------+---------------------------------
 pg_buffercache | 1.5     | public     | examine the shared buffer cache
 plpgsql        | 1.0     | pg_catalog | PL/pgSQL procedural language
(2 rows)

postgres=# \d pg_buffercache
                 View "public.pg_buffercache"
      Column      |   Type   | Collation | Nullable | Default
------------------+----------+-----------+----------+---------
 bufferid         | integer  |           |          | 
 relfilenode      | oid      |           |          | 
 reltablespace    | oid      |           |          | 
 reldatabase      | oid      |           |          | 
 relforknumber    | smallint |           |          | 
 relblocknumber   | bigint   |           |          | 
 isdirty          | boolean  |           |          | 
 usagecount       | smallint |           |          | 
 pinning_backends | integer  |           |          | 

安装扩展后,该函数也将存在:

postgres=# \dfS *evict*
                               List of functions
 Schema |         Name         | Result data type | Argument data types | Type 
--------+----------------------+------------------+---------------------+------
 public | pg_buffercache_evict | boolean          | integer             | func
(1 row)

为了将某些内容加载到缓冲区缓存中,我们将使用 pg_prewarm 扩展,并完整地加载我们随后创建的表:

create extension pg_prewarm;

create table t ( a int, b text );

insert into t select i, i::text from generate_series(1,10000) i;

select pg_prewarm ( 't', 'buffer', 'main', null, null );
 pg_prewarm
------------
         54
(1 row)

select pg_relation_filepath('t');
 pg_relation_filepath
----------------------
 base/5/16401
(1 row)

select count(*) from pg_buffercache where relfilenode = 16401;
 count
-------
    58
(1 row)

如果您想知道为什么缓冲区缓存中缓存了 58 个块,但我们只加载了 54 个,这是因为可见性映射空闲空间映射的原因:

select relforknumber from pg_buffercache where relfilenode = 16401 and relforknumber != 0;
 relforknumber 
---------------
             1
             1
             1
             2
(4 rows)

使用新的 pg_buffercache_evict() 函数,我们现在可以从缓存中完全驱逐该表的缓冲区,这将导致恰好驱逐 58 个块:

select pg_buffercache_evict(bufferid) from pg_buffercache where relfilenode = 16401;
 pg_buffercache_evict 
----------------------
 t
 t
 ...
 t
 t
(58 rows)

再次进行检查,确认所有缓存块均已消失:

select count(*) from pg_buffercache where relfilenode = 16401;
 count
-------
     0
(1 row)

非常不错的特性,感谢所有参与的社区人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/13453eedd3f692f8dcf8e334396eee84f00fdde2