由 John Doe 六月 9, 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 相关统计信息的新视图。
我们要查看的第一个统计数据是 “extends”。在此之前,我们需要理解 “extends” 的含义。其背后的原理很简单。考虑如下的一张空表:
create table t ( a int );
这个表目前在磁盘上分配了多少空间?让我们来查询 PostgreSQL 获取此信息:
select pg_relation_size('t');
pg_relation_size
------------------
0
(1 row)
该结果表示,目前该表不占用任何磁盘空间。这可以很容易地通过下面方式验证:
select pg_relation_filepath('t');
pg_relation_filepath
----------------------
base/5/16438
(1 row)
$ ls -l $PGDATA/base/5/16438
-rw------- 1 postgres postgres 0 Jul 27 15:55 /db/pgsql/data/base/5/16438
PostgreSQL 和文件系统都报告了相同的大小。如果我们添加一行会发生什么?
insert into t values (1);
select pg_relation_size('t');
pg_relation_size
------------------
8192
(1 row)
$ ls -l $PGDATA/base/5/16438
-rw------- 1 postgres postgres 8192 Jul 27 15:59 /db/pgsql/data/base/5/16438
我们看到了表大小是 8k,这是实例的数据块大小:
show block_size;
block_size
------------
8192
(1 row)
那么,什么是 “extends”?当 PostgreSQL 需要向关系中添加更多行,而现有块中没有剩余空间时,就需要添加一个新块。上面的例子中有一次 “extends”,对应 8k,也就是一个块。
现在,新的 pg_stat_io 视图会跟踪这些发生的 “extends”。让我们通过一些简单的例子,来理解这些数字。首先,我们从头开始,截断一个已有的表:
truncate t;
$ ls -l $PGDATA/base/5/16438
-rw------- 1 postgres postgres 0 Jul 27 16:09 /db/pgsql/data/base/5/16438
这使我们回到磁盘大小为零的状态。pg_stat_io 视图包含以下列:
postgres=# \d pg_stat_io
View "pg_catalog.pg_stat_io"
Column | Type | Collation | Nullable | Default
----------------+--------------------------+-----------+----------+---------
backend_type | text | | |
object | text | | |
context | text | | |
reads | bigint | | |
read_time | double precision | | |
writes | bigint | | |
write_time | double precision | | |
writebacks | bigint | | |
writeback_time | double precision | | |
extends | bigint | | |
extend_time | double precision | | |
op_bytes | bigint | | |
hits | bigint | | |
evictions | bigint | | |
reuses | bigint | | |
fsyncs | bigint | | |
fsync_time | double precision | | |
stats_reset | timestamp with time zone | | |
因为我们只对我们的会话感兴趣(没有其他客户端会话连接到此实例),并且我们只想查看有关 “extends” 的统计信息,并且我们只对关系类型的对象的统计信息感兴趣,所以我们可以使用下面命令来查看我们触发的表扩展:
select backend_type,extends,op_bytes
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | extends | op_bytes
----------------+---------+----------
client backend | 9 | 8192
(1 row)
由于我们已经有了一些统计数据,让我们来重置下统计中的计数值:
select pg_stat_reset_shared('io');
pg_stat_reset_shared
----------------------
(1 row)
select backend_type,extends,op_bytes
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | extends | op_bytes
----------------+---------+----------
client backend | 0 | 8192
(1 row)
“op_bytes” 表示每个 I/O 单元的字节数,其对应 “read”、“write” 或 “extend”。这也是 8k 的块大小。
如果我们在小表 “t” 中生成 1000 行,会发生什么?
insert into t select * from generate_series(1,1000);
select backend_type,extends,op_bytes
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | extends | op_bytes
----------------+---------+----------
client backend | 8 | 8192
(1 row)
这样出现了 8 次扩展,8 * 8192 = 65536 字节。如果我们查询关系表的大小,PostgreSQL 应该会返回相同的值:
select pg_relation_size('t');
pg_relation_size
------------------
40960
(1 row)
select pg_relation_filepath('t');
pg_relation_filepath
----------------------
base/5/16441
(1 row)
$ ls -l $PGDATA/base/5/16441*
-rw------- 1 postgres postgres 40960 Jul 28 07:11 /db/pgsql/data/base/5/16441
-rw------- 1 postgres postgres 24576 Jul 28 07:11 /db/pgsql/data/base/5/16441_fsm
40960 + 24576 = 65536 字节。不仅表文件需要扩展,空闲空间映射也需要扩展,扩展空间的总数刚好是两者之和。
最后,关于扩展,还有一个额外的统计数据:“extend_time”。这是扩展关系文件所花费的时间(以毫秒为单位)。默认情况下,它始终为零:
select backend_type,extends,extend_time,op_bytes
from pg_stat_io
where backend_type = 'client backend'
and context = 'normal'
and object ='relation';
backend_type | extends | extend_time | op_bytes
----------------+---------+-------------+----------
client backend | 8 | 0 | 8192
(1 row)
要获取这些统计数据,您需要启用 track_io_timing。
非常不错的体验。感谢社区的所有相关人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/a9c70b46dbe152e094f137f7e6ba9cd3a638ee25