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
为特定数据库启用。
pg_stat_statements
视图 #模块收集的统计信息通过名为 pg_stat_statements
的视图提供。此视图包含数据库 ID、用户 ID、查询 ID 和它是否为顶级语句(最多模块可以跟踪的不同语句的最大数量)的每个不同组合的一行。该视图的列显示在 表 F.21 中。
表 F.21. pg_stat_statements
列
列 类型 描述 |
---|
执行语句的用户 OID |
执行语句的数据库 OID |
如果查询作为顶级语句执行,则为真(如果 |
用于识别相同规范化查询的哈希码。 |
代表性语句的文本 |
语句被计划的次数(如果启用了 |
计划语句的总时间,以毫秒为单位(如果启用了 |
计划语句的最短时间,以毫秒为单位。如果禁用了 |
计划语句的最长时间,以毫秒为单位。如果禁用了 |
计划语句的平均时间,以毫秒为单位(如果启用了 |
计划语句所用时间的总体标准差,以毫秒为单位(如果启用了 |
语句被执行的次数 |
执行语句的总时间,以毫秒为单位 |
执行语句的最短时间,以毫秒为单位,此字段将为零,直到此语句在使用 |
执行语句的最长时间,以毫秒为单位,此字段将为零,直到此语句在使用 |
执行语句的平均时间,以毫秒为单位 |
执行语句所用时间的总体标准差,以毫秒为单位 |
语句检索或影响的总行数 |
语句共享块缓存命中的总数 |
语句读取的共享块总数 |
语句弄脏的共享块总数 |
语句写入的共享块总数 |
语句本地块缓存命中的总数 |
语句读取的本地块总数 |
语句弄脏的本地块总数 |
语句写入的本地块总数 |
语句读取的临时块总数 |
语句写入的临时块总数 |
语句读取共享块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零) |
语句写入共享块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零) |
语句读取本地块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零) |
语句写入本地块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零) |
语句读取临时文件块的总时间,以毫秒为单位(如果启用了track_io_timing,否则为零) |
语句写入临时文件块的总时间,以毫秒为单位(如果启用了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
,则应参考其文档以了解详细信息。
当常量的值已被忽略以用于将查询与其他查询匹配时,该常量在pg_stat_statements
显示中被替换为参数符号,例如$1
。查询文本的其余部分是第一个具有与pg_stat_statements
条目关联的特定queryid
哈希值的查询的文本。
可以在pg_stat_statements
中观察到可以应用规范化的查询的常量值,尤其是在条目释放率很高的情况下。为了降低发生这种情况的可能性,请考虑增加pg_stat_statements.max
。下面在第 F.30.2 节中讨论的pg_stat_statements_info
视图提供了有关条目释放的统计信息。
在某些情况下,具有明显不同文本的查询可能会合并到单个pg_stat_statements
条目中。通常,这只会发生在语义上等效的查询中,但存在很小的几率哈希冲突会导致不相关的查询合并到一个条目中。(但是,对于属于不同用户或数据库的查询,这不可能发生。)
由于queryid
哈希值是在查询的后解析分析表示上计算的,因此反之亦然:如果查询由于不同的search_path
设置等因素而具有不同的含义,则具有相同文本的查询可能会显示为单独的条目。
pg_stat_statements
的使用者可能希望使用queryid
(可能与dbid
和userid
结合使用)作为每个条目的更稳定和可靠的标识符,而不是其查询文本。但是,必须了解围绕queryid
哈希值的稳定性只有有限的保证。由于标识符是从后解析分析树派生的,因此其值是除其他事项外,此表示中出现的内部对象标识符的函数。这有一些违反直觉的含义。例如,如果在两个查询的执行之间删除并重新创建了表,则pg_stat_statements
会将两个看似相同的查询视为不同的查询。哈希过程也对机器架构和平台的其他方面差异敏感。此外,不能假设queryid
在PostgreSQL的主要版本之间是稳定的。
参与基于物理 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
视图中所有现有条目都将显示空query
字段,尽管与每个queryid
关联的统计信息将保留。如果发生这种情况,请考虑减少pg_stat_statements.max
以防止再次发生。
plans
和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) returns timestamp with time zone
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
参数将返回空值)。此功能旨在支持可能希望避免重复检索不确定长度的查询文本的开销的外部工具。此类工具可以改为自行缓存观察到的每个条目的第一个查询文本,因为这是 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 |
板垣貴弘 <[email protected]>
。Peter Geoghegan <[email protected]>
添加了查询规范化。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步说明,请使用 此表单 报告文档问题。