受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

F.30. pg_stat_statements — 跟踪 SQL 计划和执行统计信息 #

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

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

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

F.30.1. pg_stat_statements 视图 #

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

Table F.21. pg_stat_statements

列类型

描述

userid oid (references pg_authid.oid)

执行该语句的用户的 OID

dbid oid (references pg_database.oid)

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

toplevel bool

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

queryid bigint

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

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 被禁用, 或者计数器曾通过 pg_stat_statements_reset 函数 以 minmax_only 参数设为 true 的方式重置,并且此后从未再次进行计划,则该字段为零。

max_plan_time double precision

对该语句进行计划所花费的最长时间,单位为毫秒。 如果 pg_stat_statements.track_planning 被禁用, 或者计数器曾通过 pg_stat_statements_reset 函数 以 minmax_only 参数设为 true 的方式重置,并且此后从未再次进行计划,则该字段为零。

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

执行该语句所花费的最短时间,单位为毫秒。 如果通过 pg_stat_statements_reset 函数执行重置,并将 minmax_only 参数设为 true, 则在该语句此后首次执行之前,该字段为零。

max_exec_time double precision

执行该语句所花费的最长时间,单位为毫秒。 如果通过 pg_stat_statements_reset 函数执行重置,并将 minmax_only 参数设为 true, 则在该语句此后首次执行之前,该字段为零。

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

该语句写入的临时块总数

shared_blk_read_time double precision

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

shared_blk_write_time double precision

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

local_blk_read_time double precision

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

local_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

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

jit_deform_count bigint

该语句 JIT 编译的元组拆解函数总数

jit_deform_time double precision

该语句对元组拆解函数进行 JIT 编译所花费的总时间,单位为毫秒

stats_since timestamp with time zone

开始为该语句收集统计信息的时间

minmax_stats_since timestamp with time zone

开始为该语句收集最小/最大统计信息的时间 (字段 min_plan_timemax_plan_timemin_exec_timemax_exec_time


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

可进行计划的查询(即 SELECTINSERTUPDATEDELETEMERGE) 以及实用命令,只要根据内部哈希计算得出的查询结构相同,就会合并为单条 pg_stat_statements 记录。通常,如果两个查询在语义上等价, 仅在查询中出现的字面常量值不同,则会被视为相同。

Note

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

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

对于可以应用规范化的查询,有时仍可能在 pg_stat_statements 中看到常量值,尤其是在条目被频繁释放时。 要降低这种情况发生的可能性,可考虑增大 pg_stat_statements.max。下文 Section F.30.2 讨论的 pg_stat_statements_info 视图提供了有关条目释放的统计信息。

single pg_stat_statements entry. Normally this will happen only for semantically equivalent queries, but there is a small chance of hash collisions causing unrelated queries to be merged into one entry. (不过,这种情况不会发生在属于不同用户或不同数据库的查询之间。)

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

pg_stat_statements 的使用者可能希望使用 queryid(也许再结合 dbiduserid)作为每条记录比查询文本更稳定、 更可靠的标识符。不过,必须理解的是, queryid 哈希值的稳定性只得到有限保证。 由于该标识符源自解析分析后的语法树,它的取值除其他因素外,还取决于该表示形式中出现的内部对象标识符。 这会带来一些反直觉的结果。例如,如果两次查询执行之间, queries to be distinct, if they reference a table that was dropped and recreated between the executions of the two queries. 哈希过程也对机器架构差异以及平台的其他方面很敏感。 此外,也不能安全地假设 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 以避免再次发生。

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

F.30.2. pg_stat_statements_info 视图 #

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

Table F.22. pg_stat_statements_info

列类型

描述

dealloc bigint

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

stats_reset timestamp with time zone

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


F.30.3. 函数 #

pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint, minmax_only boolean) returns timestamp with time zone

pg_stat_statements_reset 会丢弃到目前为止由 pg_stat_statements 收集、且与指定 useriddbidqueryid 对应的统计信息。如果某个参数未指定, 则该参数使用默认值 0(无效),并重置与其他参数匹配的统计信息。 如果未指定任何参数,或者所有指定参数均为 0(无效),则会丢弃全部统计信息。 如果 pg_stat_statements 视图中的全部统计信息都被丢弃,则也会重置 pg_stat_statements_info 视图中的统计信息。 当 minmax_onlytrue 时, 只会重置计划和执行时间的最小值与最大值(即 min_plan_timemax_plan_timemin_exec_timemax_exec_time 字段)。minmax_only 参数的默认值为 false。上一次执行最小值/最大值重置的时间会显示在 pg_stat_statements 视图的 minmax_stats_since 字段中。该函数返回重置发生的时间。 如果实际执行了相应的重置,该时间会保存到 pg_stat_statements_info 视图的 stats_reset 字段,或者 pg_stat_statements 视图的 minmax_stats_since 字段中。 默认情况下,该函数只能由超级用户执行。 可使用 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.30.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 控制该模块是否跟踪实用命令。 实用命令是除 SELECTINSERTUPDATEDELETEMERGE 之外的所有命令。 默认值为 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.30.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.30.6. 作者 #

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

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。