由 John Doe 六月 10, 2025
你知道如何确定数据库共享缓冲区的大小吗?其实,这可以通过缓存页的换出频率来确定。
特性提交日志
添加 pg_stat_io 视图,提供更详细的 IO 统计信息。
该视图中的行会展示出特定的后端类型、IO 目标对象、IO 上下文组合,例如客户端后端对共享缓冲区中持久化关系的操作,视图中的每列表示已完成的 IO 操作总数(例如写入次数)。例如,视图中的一个值可能会表示自上次统计重置以来,客户端后端从共享缓冲区写入关系数据的块数。
为了便于跟踪 WAL 日志的 IO 和非数据块的 IO(如临时文件 IO),“op_bytes” 列指定了给定行中 “reads”、“writes” 和 “extends” 列的单位。
对于从未出现的 IO 操作、后端类型、目标对象和上下文的组合,视图将完全省略相关行。例如,检查点进程(checkpointer)永远不会对临时关系执行操作。
类似地,如果某个组合从未发生过某个 IO 操作,则该 IO 操作的相关值将显示为 null,以区分于观察到 0 次 IO 操作的情况。例如,后台写入进程(bgwriter)不会执行读取操作。
请注意,视图中的某些值与 pg_stat_bgwriter 中的字段存在冗余(例如 buffers_backend)。目前为了向后兼容,这些字段将保留。
讨论:https://postgr.es/m/20200124195226.lth52iydq2n2uilq@alap3.anarazel.de
示例
PostgreSQL 新版本引入了一个用于跟踪 I/O 相关统计信息的新视图。
下面,让我们来研究下 “evictions” 统计数。在深入细节之前,我们先来了解一下什么是换出?假设缓冲区缓存当前已满,PostgreSQL 需要从磁盘(或 OS 缓存)加载另一个块。为此,必须换出当前缓存在缓冲区中的块,以便为要加载的块腾出空间。这就是该统计数的作用:跟踪换出次数。该统计数可以帮助您确定共享缓冲区(shared_buffers)的大小是否合适。
PostgreSQL 共享缓冲区的默认大小为 128MB,这是我们目前的大小:
show shared_buffers;
shared_buffers
----------------
128MB
(1 row)
使用 pg_buffercache 扩展,我们可以在实例启动后立即检查当前使用了多少个块:
select count(*) from pg_buffercache where relfilenode is not null;
count
-------
287
(1 row)
这意味着当前使用了 287 * 8192(这是实例中数据块的大小)= 2351104 字节,略小于 2.3MB。
这也意味着,此时在 pg_stat_io 视图中不会看到任何换出:
select backend_type,evictions
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | evictions
----------------+-----------
client backend | 0
(1 row)
要查看缓存的换出现象,我们需要加载大约 126MB 的数据,因此让我们这样做:
create table t ( a int, b text, c text, d text );
insert into t select i, md5(i::text), md5(i::text), md5(i::text)
from generate_series(1,1000000) i;
select pg_size_pretty(pg_relation_size('t'));
pg_size_pretty
----------------
128 MB
(1 row)
这些数据应该足以让我们看到一些换出行为:
select backend_type,evictions
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | evictions
----------------+-----------
client backend | 288
(1 row)
让我们看一下我们的表当前缓存了多少内容:
select c.relname, count(*) AS buffers
from pg_class c
inner join pg_buffercache b
on b.relfilenode=c.relfilenode
inner join pg_database d
on (b.reldatabase=d.oid AND d.datname=current_database())
where c.relname = 't'
group by c.relname;
relname | buffers
---------+---------
t | 16237
(1 row)
这是 (16237 * 8) / 1024,大约 127 MB 的数据。由于表大小为 128MB,而缓冲区缓存中已经使用了大约 2MB,因此需要进行一些换出操作。288 次换出意味着略大于 2MB,因此这似乎是一致的。
如果我们像下面这样创建该表的副本,然后再次查看换出情况,这次我们会看到如下内容:
create table t2 as select * from t;
select backend_type,evictions,context
from pg_stat_io
where backend_type = 'client backend'
and object ='relation';
backend_type | evictions | context
----------------+-----------+-----------
client backend | 32 | bulkread
client backend | 2048 | bulkwrite
client backend | 323 | normal
client backend | 0 | vacuum
批量读取和批量写入意味着,数据块会从共享缓冲区中取出,并被放置在环形缓冲区中,以供批量 I/O 操作使用。对于 VACUUM 操作也是如此:
update t set d = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
vacuum t;
select backend_type,evictions,context
from pg_stat_io
where backend_type = 'client backend'
and object ='relation';
backend_type | evictions | context
----------------+-----------+-----------
client backend | 16102 | bulkread
client backend | 2048 | bulkwrite
client backend | 18611 | normal
client backend | 32 | vacuum
总结一下:当你在“正常”环境下看到大量换出时,这可能提示你的共享缓冲区太小。如果几乎没有看到换出,则可能意味着你的共享缓冲区过大。
非常不错的体验。感谢社区的所有相关人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/a9c70b46dbe152e094f137f7e6ba9cd3a638ee25