由 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