支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: 开发版
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4

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

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

必须通过将 pg_stat_statements 添加到 postgresql.conf 中的 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 以及它是否是顶级语句的每个不同组合(最多为模块可以跟踪的最大不同语句数)包含一行。该视图的列显示在 表 F.21 中。

表 F.21. pg_stat_statements

列 类型

描述

userid oid (引用 pg_authid.oid)

执行该语句的用户的 OID

dbid oid (引用 pg_database.oid)

执行该语句的数据库的 OID

toplevel bool

如果查询是作为顶级语句执行的,则为 True(如果 pg_stat_statements.track 设置为 top,则始终为 true)

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,或者使用 minmax_only 参数设置为 truepg_stat_statements_reset 函数重置计数器后,从未计划过,则此字段将为零。

max_plan_time double precision

计划该语句所花费的最长时间,以毫秒为单位。如果禁用了 pg_stat_statements.track_planning,或者使用 minmax_only 参数设置为 truepg_stat_statements_reset 函数重置计数器后,从未计划过,则此字段将为零。

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

执行该语句所花费的最短时间,以毫秒为单位,在使用 minmax_only 参数设置为 truepg_stat_statements_reset 函数执行重置后,此语句首次执行之前,此字段将为零

max_exec_time double precision

执行该语句所花费的最长时间,以毫秒为单位,在使用 minmax_only 参数设置为 truepg_stat_statements_reset 函数执行重置后,此语句首次执行之前,此字段将为零

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 条目中。通常,如果两个查询在语义上等效,除了查询中出现的字面常数的值不同,则认为它们是相同的。

注意

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

当为了将查询与其他查询匹配而忽略常量的值时,该常量将在 pg_stat_statements 显示中被参数符号替换,例如 $1。其余的查询文本是具有与 pg_stat_statements 条目关联的特定 queryid 哈希值的第一个查询的文本。

可以应用规范化的查询可能会在 pg_stat_statements 中观察到带有常量值的情况,尤其是在条目解除分配率很高时。为了减少这种情况发生的可能性,请考虑增加 pg_stat_statements.maxpg_stat_statements_info 视图,在下面的第 F.30.2 节中讨论,提供了有关条目解除分配的统计信息。

在某些情况下,具有明显不同文本的查询可能会合并到单个 pg_stat_statements 条目中。通常,这只会发生在语义等效的查询中,但哈希冲突可能会导致不相关的查询合并到一个条目中。(但是,对于属于不同用户或数据库的查询,不会发生这种情况。)

由于 queryid 哈希值是在查询的解析后分析表示上计算的,因此也可能出现相反的情况:如果由于 search_path 设置不同等因素导致它们的含义不同,则具有相同文本的查询可能会显示为单独的条目。

pg_stat_statements 的使用者可能希望使用 queryid(可能与 dbiduserid 结合使用)作为每个条目的更稳定和可靠的标识符,而不是其查询文本。但是,重要的是要了解围绕 queryid 哈希值的稳定性只有有限的保证。由于标识符是从解析后分析树派生的,因此其值是此表示中出现的内部对象标识符等因素的函数。这有一些违反直觉的含义。例如,如果两个查询引用在两个查询执行之间被删除并重新创建的表,则 pg_stat_statements 会认为两个表面上相同的查询是不同的。哈希过程也对机器体系结构和平台的其他方面中的差异敏感。此外,假设 queryidPostgreSQL 的主要版本之间保持稳定是不安全的。

基于物理 WAL 重放参与复制的两个服务器可以预期对同一查询具有相同的 queryid 值。但是,逻辑复制方案不保证副本在所有相关细节中都相同,因此 queryid 将不是用于累积一组逻辑副本成本的有用标识符。如果有疑问,建议直接测试。

通常,可以假设 queryid 值在 PostgreSQL 的次要版本发布之间是稳定的,前提是实例在相同的机器体系结构上运行并且目录元数据细节匹配。仅作为最后的手段,才会在次要版本之间破坏兼容性。

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

代表性查询文本保存在外部磁盘文件中,不占用共享内存。因此,即使是很长的查询文本也可以成功存储。但是,如果积累了许多长查询文本,则外部文件可能会变得非常大。如果发生这种情况,作为恢复方法,pg_stat_statements 可能会选择放弃查询文本,此时 pg_stat_statements 视图中的所有现有条目都将显示 null 的 query 字段,但与每个 queryid 关联的统计信息将保留。如果发生这种情况,请考虑减少 pg_stat_statements.max 以防止再次发生。

planscalls 并不总是期望匹配,因为计划和执行统计信息在其各自的结束阶段更新,并且仅针对成功的操作更新。例如,如果一个语句成功计划但在执行阶段失败,则只会更新其计划统计信息。如果因为使用了缓存的计划而跳过了计划,则只会更新其执行统计信息。

F.30.2. pg_stat_statements_info 视图 #

pg_stat_statements 模块本身的统计信息会进行跟踪,并通过名为 pg_stat_statements_info 的视图提供。此视图仅包含一行。该视图的列显示在表 F.22中。

表 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 添加了查询规范化 .

提交更正

如果您在文档中发现任何不正确、与您特定功能的使用经验不符或需要进一步澄清的地方,请使用此表单报告文档问题。