↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4

F.32. pg_stat_statements #

pg_stat_statements 模块提供了一种机制,用于跟踪服务器执行的所有 SQL 语句的计划和执行统计信息。

由于该模块需要额外的共享内存,因此必须通过在 postgresql.conf 中将 pg_stat_statements 加入 shared_preload_libraries 来加载该模块。这意味着添加或移除此模块都需要重启服务器。此外,要让该模块生效,还必须启用查询标识符计算;如果 compute_query_id 被设置为 auto 或 on,或者加载了任何计算查询标识符的第三方模块,这一步就会自动完成。

启用 pg_stat_statements 后,它会跟踪该服务器上所有数据库的统计信息。为了访问和操作这些统计信息,该模块提供了视图 pg_stat_statements 和 pg_stat_statements_info,以及实用函数 pg_stat_statements_reset 和 pg_stat_statements。这些对象并非全局可用,但可以通过 CREATE EXTENSION pg_stat_statements 在特定数据库中启用。

F.32.1. pg_stat_statements 视图 #

该模块收集的统计信息可通过名为 pg_stat_statements 的视图获取。对于数据库 ID、用户 ID、查询 ID 以及该语句是否为顶层语句的每一种不同组合,该视图都包含一行(最多保存到该模块能够跟踪的不同语句的最大数量)。该视图的列见表 F.20。

表 F.20. pg_stat_statements 列

列类型

描述

userid oid(引用 pg_authid.oid)

执行该语句的用户的 OID

dbid oid(引用 pg_database.oid)

执行该语句所在数据库的 OID

toplevel bool

如果该查询作为顶层语句执行,则为真(如果 pg_stat_statements.track 设置为 top,则始终为真)

queryid bigint

用于标识相同规范化查询的 hash 码。

query text

某个代表性语句的文本

plans bigint

对该语句进行计划的次数(如果启用了 pg_stat_statements.track_planning,否则为零)

total_plan_time double precision

对该语句进行计划所花费的总时间,单位为毫秒(如果启用了 pg_stat_statements.track_planning,否则为零)

min_plan_time double precision

对该语句进行计划所花费的最短时间,单位为毫秒(如果启用了 pg_stat_statements.track_planning,否则为零)

max_plan_time double precision

对该语句进行计划所花费的最长时间,单位为毫秒(如果启用了 pg_stat_statements.track_planning,否则为零)

mean_plan_time double precision

对该语句进行计划所花费的平均时间,单位为毫秒(如果启用了 pg_stat_statements.track_planning,否则为零)

stddev_plan_time double precision

对该语句进行计划所花费时间的总体标准差,单位为毫秒(如果启用了 pg_stat_statements.track_planning,否则为零)

calls bigint

该语句执行的次数

total_exec_time double precision

执行该语句所花费的总时间,单位为毫秒

min_exec_time double precision

执行该语句所花费的最短时间,单位为毫秒

max_exec_time double precision

执行该语句所花费的最长时间,单位为毫秒

mean_exec_time double precision

执行该语句所花费的平均时间,单位为毫秒

stddev_exec_time double precision

执行该语句所花费时间的总体标准差,单位为毫秒

rows bigint

该语句检索到或影响的总行数

shared_blks_hit bigint

该语句的共享块缓存命中总数

shared_blks_read bigint

该语句读取的共享块总数

shared_blks_dirtied bigint

被该语句弄脏的共享块总数

shared_blks_written bigint

该语句写入的共享块总数

local_blks_hit bigint

该语句的本地块缓存命中总数

local_blks_read bigint

该语句读取的本地块总数

local_blks_dirtied bigint

被该语句弄脏的本地块总数

local_blks_written bigint

该语句写入的本地块总数

temp_blks_read bigint

该语句读取的临时块总数

temp_blks_written bigint

该语句写入的临时块总数

blk_read_time double precision

该语句读取数据文件块所花费的总时间,单位为毫秒(如果启用了 track_io_timing,否则为零)

blk_write_time double precision

该语句写入数据文件块所花费的总时间,单位为毫秒(如果启用了 track_io_timing,否则为零)

temp_blk_read_time double precision

该语句读取临时文件块所花费的总时间,单位为毫秒(如果启用了 track_io_timing,否则为零)

temp_blk_write_time double precision

该语句写入临时文件块所花费的总时间,单位为毫秒(如果启用了 track_io_timing,否则为零)

wal_records bigint

该语句生成的 WAL 记录总数

wal_fpi bigint

该语句生成的 WAL 整页镜像总数

wal_bytes numeric

该语句生成的 WAL 总量,单位为字节

jit_functions bigint

该语句 JIT 编译的函数总数

jit_generation_time double precision

该语句生成 JIT 代码所花费的总时间,单位为毫秒

jit_inlining_count bigint

函数被内联的次数

jit_inlining_time double precision

该语句对函数进行内联所花费的总时间,单位为毫秒

jit_optimization_count bigint

该语句被优化的次数

jit_optimization_time double precision

该语句进行优化所花费的总时间,单位为毫秒

jit_emission_count bigint

生成代码的次数

jit_emission_time double precision

该语句用于生成代码的总时间,单位为毫秒


出于安全原因,只有超级用户以及具有 pg_read_all_stats 角色权限的角色才允许查看其他用户执行的查询的 SQL 文本和 queryid。不过,只要该视图已经安装在其数据库中,其他用户仍然可以查看统计信息。

可规划的查询(即 SELECT、INSERT、UPDATE、DELETE 和 MERGE)只要根据内部 hash 计算得出的查询结构相同,就会合并为单条 pg_stat_statements 记录。通常,如果两个查询在语义上等价,仅在查询中出现的字面常量值不同,则会被视为相同。而实用命令(即所有其他命令)则严格按照其查询文本字符串进行比较。

注意

以下关于常量替换和 queryid 的细节,仅在启用了 compute_query_id 时适用。如果改用外部模块来计算 queryid,则应参阅该模块的文档了解细节。

当为了将某个查询与其他查询匹配而忽略常量值时,在 pg_stat_statements 的显示中,该常量会被参数符号(例如 $1)替换。查询文本的其余部分则取自第一个具有该特定 queryid hash 值、并与该 pg_stat_statements 记录相关联的查询。

在某些情况下,文本明显不同的查询也可能被合并到同一条 pg_stat_statements 记录中。通常,这只会发生在语义等价的查询之间,但也存在很小的概率由于 hash 冲突而把不相关的查询合并为同一条记录。(不过,这种情况不会发生在属于不同用户或不同数据库的查询之间。)

由于 queryid hash 值是根据查询经过解析分析后的表示形式计算出来的,相反的情况也可能发生:文本完全相同的查询,如果由于不同的 search_path 设置等因素而具有不同含义,就可能显示为不同的记录。

pg_stat_statements 的使用者可能希望使用 queryid(也许再结合 dbid 和 userid)作为每条记录比查询文本更稳定、更可靠的标识符。不过,必须理解的是,queryid hash 值的稳定性只得到有限保证。由于该标识符源自解析分析后的语法树,它的取值除其他因素外,还取决于该表示形式中出现的内部对象标识符。这会带来一些反直觉的结果。例如,如果两次查询执行之间,所引用的某张表被删除并重新创建,那么 pg_stat_statements 会将两条看似完全相同的查询视为不同。hash 运算过程也对机器架构差异以及平台的其他方面很敏感。此外,也不能安全地假设 queryid 会在 PostgreSQL 的主版本之间保持稳定。

在基于物理 WAL 重放的复制中,参与复制的两个服务器对于同一查询通常可以期待获得相同的 queryid 值。然而,逻辑复制方案并不承诺在所有相关细节上保持副本完全一致,因此 queryid 并不适合作为在一组逻辑副本之间累积开销的标识符。如有疑问,建议直接测试。

通常可以假定 queryid 值在 PostgreSQL 的小版本发布之间保持稳定,前提是实例运行在相同的机器架构上,并且目录元数据细节一致。只有在迫不得已时,才会在小版本之间打破这种兼容性。

在代表性查询文本中,用于替换常量的参数符号从原始查询文本中最高的 $n 参数之后的下一个数字开始,如果原文中没有,则从 $1 开始。值得注意的是,在某些情况下,可能存在会影响这一编号的隐藏参数符号。例如,PL/pgSQL 使用隐藏参数符号将函数局部变量的值插入查询中,因此像 SELECT i + 1 INTO j 这样的 PL/pgSQL 语句,其代表性文本可能是 SELECT i + $2。

代表性查询文本保存在外部磁盘文件中,因此不消耗共享内存。即使非常长的查询文本也可以成功存储。不过,如果积累了很多长查询文本,该外部文件可能会膨胀到难以管理的大小。若发生这种情况,作为一种恢复措施,pg_stat_statements 可能会选择丢弃这些查询文本,这样 pg_stat_statements 视图中的现有记录都会显示 query 字段为 null,但与各个 queryid 相关的统计信息仍会保留。如果发生这种情况,可考虑减小 pg_stat_statements.max 以避免再次发生。

plans 和 calls 并不总是相同,因为计划和执行统计信息分别在各自阶段结束时更新,并且只针对成功的操作。例如,如果某条语句计划成功但在执行阶段失败,则只会更新其计划统计信息。如果由于使用了缓存计划而跳过了计划,则只会更新其执行统计信息。

F.32.2. pg_stat_statements_info 视图 #

pg_stat_statements 模块自身的统计信息也会被跟踪,并通过名为 pg_stat_statements_info 的视图提供。该视图仅包含一行。其列见表 F.21。

表 F.21. pg_stat_statements_info 列

列类型

描述

dealloc bigint

由于观察到的不同语句超过了 pg_stat_statements.max,执行次数最少的语句对应的 pg_stat_statements 记录被释放的总次数

stats_reset timestamp with time zone

上一次重置 pg_stat_statements 视图中全部统计信息的时间。


F.32.3. 函数 #

pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void

pg_stat_statements_reset 会丢弃到目前为止由 pg_stat_statements 收集、且与指定 userid、dbid 和 queryid 对应的统计信息。如果某个参数未指定,则该参数使用默认值 0(无效),并重置与其他参数匹配的统计信息。如果未指定任何参数,或者所有指定参数均为 0(无效),则会丢弃全部统计信息。如果 pg_stat_statements 视图中的全部统计信息都被丢弃,则也会重置 pg_stat_statements_info 视图中的统计信息。默认情况下,该函数只能由超级用户执行。可使用 GRANT 将访问权限授予其他用户。

pg_stat_statements(showtext boolean) returns setof record

pg_stat_statements 视图是根据一个同名的 pg_stat_statements 函数定义的。客户端也可以直接调用 pg_stat_statements 函数,并通过指定 showtext := false 来省略查询文本(也就是说,与该视图 query 列对应的 OUT 参数会返回 null)。这一特性旨在支持某些外部工具,这些工具可能希望避免反复获取长度不定的查询文本所带来的开销。这些工具可以自行缓存每条记录第一次观察到的查询文本,因为这正是 pg_stat_statements 本身所做的事情,然后只在需要时再获取查询文本。由于服务器会把查询文本存储在文件中,这种做法在反复检查 pg_stat_statements 数据时可以减少物理 I/O。

F.32.4. 配置参数 #

pg_stat_statements.max (integer)

pg_stat_statements.max 是该模块可跟踪的语句最大数量(即 pg_stat_statements 视图中的最大行数)。如果观察到的不同语句超过该数量,则执行次数最少的语句的信息会被丢弃。此类信息被丢弃的次数可以在 pg_stat_statements_info 视图中看到。默认值为 5000。该参数只能在服务器启动时设置。

pg_stat_statements.track (enum)

pg_stat_statements.track 控制该模块统计哪些语句。指定 top 可跟踪顶层语句(即直接由客户端发出的语句),指定 all 还会跟踪嵌套语句(例如在函数内调用的语句),而指定 none 则会禁用语句统计信息收集。默认值为 top。只有超级用户可以更改此设置。

pg_stat_statements.track_utility (boolean)

pg_stat_statements.track_utility 控制该模块是否跟踪实用命令。实用命令是除 SELECT、INSERT、UPDATE、DELETE 和 MERGE 之外的所有命令。默认值为 on。只有超级用户可以更改此设置。

pg_stat_statements.track_planning (boolean)

pg_stat_statements.track_planning 控制该模块是否跟踪计划操作及其持续时间。启用此参数可能会带来明显的性能损耗,尤其是在许多并发连接执行具有相同查询结构的语句,并争用更新少量 pg_stat_statements 记录时。默认值为 off。只有超级用户可以更改此设置。

pg_stat_statements.save (boolean)

pg_stat_statements.save 指定是否在服务器关闭后保留语句统计信息。如果它为 off,则在关闭时不会保存统计信息,并且在服务器启动时也不会重新载入这些统计信息。默认值为 on。该参数只能在 postgresql.conf 文件中或在服务器命令行上设置。

该模块需要与 pg_stat_statements.max 成比例的额外共享内存。注意,只要该模块被载入,就会消耗这部分内存,即使 pg_stat_statements.track 被设置为 none 也是如此。

这些参数必须在 postgresql.conf 中设置。典型用法可能如下:

# postgresql.conf
shared_preload_libraries = 'pg_stat_statements'

compute_query_id = on
pg_stat_statements.max = 10000
pg_stat_statements.track = all

F.32.5. 示例输出 #

bench=# SELECT pg_stat_statements_reset();

$ pgbench -i bench
$ pgbench -c10 -t300 bench

bench=# \x
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
calls           | 3000
total_exec_time | 25565.855387
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls           | 3000
total_exec_time | 20756.669379
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------​------------------
query           | copy pgbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 4 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls           | 3000
total_exec_time | 271.232977
rows            | 3000
hit_percent     | 98.8454011741682975
-[ RECORD 5 ]---+--------------------------------------------------​------------------
query           | alter table pgbench_accounts add primary key (aid)
calls           | 1
total_exec_time | 160.588563
rows            | 0
hit_percent     | 100.0000000000000000


bench=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements AS s
            WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2';

bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls           | 3000
total_exec_time | 20756.669379
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------​------------------
query           | copy pgbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls           | 3000
total_exec_time | 271.232977
rows            | 3000
hit_percent     | 98.8454011741682975
-[ RECORD 4 ]---+--------------------------------------------------​------------------
query           | alter table pgbench_accounts add primary key (aid)
calls           | 1
total_exec_time | 160.588563
rows            | 0
hit_percent     | 100.0000000000000000
-[ RECORD 5 ]---+--------------------------------------------------​------------------
query           | vacuum analyze pgbench_accounts
calls           | 1
total_exec_time | 136.448116
rows            | 0
hit_percent     | 99.9201915403032721

bench=# SELECT pg_stat_statements_reset(0,0,0);

bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​---------------------------
query           | SELECT pg_stat_statements_reset(0,0,0)
calls           | 1
total_exec_time | 0.189497
rows            | 1
hit_percent     |
-[ RECORD 2 ]---+--------------------------------------------------​---------------------------
query           | SELECT query, calls, total_exec_time, rows, $1 * shared_blks_hit /          +
                |                nullif(shared_blks_hit + shared_blks_read, $2) AS hit_percent+
                |           FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT $3
calls           | 0
total_exec_time | 0
rows            | 0
hit_percent     |

F.32.6. 作者 #

Takahiro Itagaki 。查询规范化功能由 Peter Geoghegan 添加。

报告文档问题

阅读 上游文档. 通过 PostgreSQL 文档反馈表单.