pg_stat_statements
模块提供了一种跟踪服务器执行的所有 SQL 语句的规划和执行的统计数据的方法。
必须通过将 pg_stat_statements
添加到 postgresql.conf
中的 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
为特定数据库启用。
pg_stat_statements
#该模块收集的统计数据通过名为 pg_stat_statements
的视图提供。该视图针对数据库 ID、用户 ID、查询 ID 的每个不重复组合包含一行,以及它是否是顶级语句(最多达到该模块可以跟踪的不重复语句数)。视图的列显示在 表 F.21 中。
表 F.21. pg_stat_statements
列
列类型 说明 |
---|
执行该语句的用户的 OID |
执行语句所在的数据库的 OID |
如果查询作为顶级语句执行,则为 True(如果 |
哈希代码用于识别完全相同的标准化查询。 |
代表性语句的文本 |
对语句进行规划的次数(如果已启用 |
规划语句的总时间(如果已启用 |
规划语句的最少时间,单位为毫秒。如果已禁用 |
规划语句的最长时间,单位为毫秒。如果已禁用 |
规划语句的平均时间,单位为毫秒(如果已启用 |
规划语句的时间总体标准差,单位为毫秒(如果已启用 |
执行语句的次数 |
执行语句的总时间,单位为毫秒 |
执行语句的最小时间,单位为毫秒,此字段在使用 |
执行语句所花费的最大时间,单位为毫秒。如果在使用 |
执行该语句平均花费的时间,单位为毫秒。 |
执行该语句所花费时间的总体标准偏差,单位为毫秒。 |
该语句检索或影响的行总数 |
该语句的共享块高速缓存命中总数。 |
该语句读取的共享块总数。 |
该语句弄脏的共享块总数。 |
该语句写入的共享块总数。 |
该语句的本地块高速缓存命中总数。 |
该语句读取的本地块总数。 |
该语句弄脏的本地块总数。 |
该语句写入的本地块总数。 |
该语句读取的 temp 块总数。 |
该语句写入的 temp 块总数。 |
该语句花在读取共享块上的总时间(如果启用了 track_io_timing,则单位为毫秒,否则为 0) |
该语句花在写入共享块上的总时间(如果启用了 track_io_timing,则单位为毫秒,否则为 0) |
该语句花在读取本地块上的总时间(如果启用了 track_io_timing,则单位为毫秒,否则为 0) |
该语句花在写入本地块上的总时间(如果启用了 track_io_timing,则单位为毫秒,否则为 0) |
该语句花在读取临时文件块上的总时间(如果启用了 track_io_timing,则单位为毫秒,否则为 0) |
该语句用于写入临时文件块的总时间,单位为毫秒(如果启用了 track_io_timing,否则为零) |
该语句生成的 WAL 记录总数 |
该语句生成的 WAL 整页映像总数 |
该语句生成的 WAL 总量,单位为字节 |
该语句 JIT 编译的函数总数 |
该语句用于生成 JIT 代码所花费的总时间,单位为毫秒 |
内联函数的次数 |
该语句用于内联函数所花费的总时间,单位为毫秒 |
该语句进行优化的次数 |
该语句用于优化所花费的总时间,单位为毫秒 |
已生成代码的次数 |
该语句用于生成代码所花费的总时间,单位为毫秒 |
该语句 JIT 编译的元组变形函数总数 |
该语句用于 JIT 编译元组变形函数所花费的总时间,单位为毫秒 |
该语句开始收集统计信息的时间 |
该语句开始收集最小/最大统计信息的时间(字段 |
出于安全原因,只有超级用户和具有 pg_read_all_stats
角色特权的角色才能查看其他用户执行的查询的 SQL 文本和 queryid
。不过,如果该视图已安装在用户的数据库中,其他用户可以看到统计信息。
可计划查询(即 SELECT
、INSERT
、UPDATE
、DELETE
和 MERGE
)以及实用程序命令合并到一个 pg_stat_statements
条目中,只要根据内部散列计算,它们具有相同的查询结构。通常,如果除查询中出现的文本常量的值之外,这两种查询在语义上是等价的,就认为这两条查询是相同的。
以下有关常量替换和 queryid
的详细信息仅当 compute_query_id 启用后才适用。如果您使用外部模块而不是计算 queryid
,您应该参考其文档以获取详细信息。
当常数的值被忽略以将查询与其他查询进行匹配时,常数将替换为参数符号,例如 $1
,在 pg_stat_statements
显示中。其余的查询文本是第一个具有与 pg_stat_statements
条目关联的特定 queryid
哈希值查询的文本。
可以在 pg_stat_statements
观察到对可应用规范化的查询具有常量值,特别是在条目释放率很高的情况下。为了降低这种情况发生的可能性,请考虑增加 pg_stat_statements.max
。在 30.2 节 讨论的 pg_stat_statements_info
视图提供有关条目释放的统计信息。
在某些情况下,文本明显不同的查询可能会合并到一个 pg_stat_statements
条目中。通常这只会发生在语义等价查询中,但散列冲突可能会导致不相关的查询合并到一个条目中,但这种情况很小。(然而,这不会发生对于属于不同用户或数据库的查询。)
由于 queryid
哈希值是在查询的后解析分析表示上计算的,因此反之亦成立:具有相同文本的查询可能会显示为单独的条目,如果它们因不同 search_path
设置等因素导致语义不同。
pg_stat_statements
消费者可能会希望将 queryid
(或许与 dbid
和 userid
结合使用)用作每个条目的更稳定且可靠的标识符,而不是其查询文本。但了解 queryid
哈希值稳定性只有有限保证这一点非常重要。由于标识符是从后解析分析树派生的,因此它的值是后解析分析树中的内部对象标识符等因素的函数。这有一些违反直觉的含义。例如,如果 pg_stat_statements
引用两条明显相同的查询并在两次查询执行之间删除再重建了该表,那么 pg_stat_statements
将认为这些查询各不相同。哈希流程还对机器架构及平台的其他方面差异很敏感。此外,我们不能安全地假设 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
字段,尽管与每个 queryid
关联的统计信息得以保留。如果发生这种情况,请考虑减少 pg_stat_statements.max
以防止再次发生。
structfield
和 calls
并不总是一致,因为规划和执行统计数据在各自的结束阶段更新,并且仅针对成功操作更新。例如,如果语句在计划阶段成功但在执行阶段失败,则只有其计划统计数据将被更新。如果规划被跳过,因为使用了缓存计划,则只有其执行统计数据将被更新。
pg_stat_statements_info
视图 #pg_stat_statements
模块的统计数据本身得到跟踪,并通过名为 pg_stat_statements_info
的视图提供。此视图仅包含一行。该视图的列显示在 表 F.22 中。
表 F.22. pg_stat_statements_info
列
列类型 说明 |
---|
由于观察到的语句比 |
|
pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint, minmax_only boolean) 返回带时区的 timestamp
pg_stat_statements_reset
丢弃当前 pg_stat_statements
收集的、对应于已指定 userid
、dbid
和 queryid
的统计信息。如果任何参数没有指定,则使用默认值 0
(无效)代替它们,而使用其他参数匹配的统计信息将会被重置。如果未指定任何参数或所有指定的参数都是 0
(无效),则它将会丢弃所有统计信息。如果 pg_stat_statements
视图中的所有统计信息都被丢弃,它还将重置 pg_stat_statements_info
视图中的统计信息。当 minmax_only
为 true
时,只有最小和最大规划和执行时间的数值会被重置(即 min_plan_time
、max_plan_time
、min_exec_time
和 max_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。
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
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 |
Takahiro Itagaki <[email protected]>
。Peter Geoghegan 添加了查询标准化 <[email protected]>
。