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 的视图获得。此视图为每个数据库 ID、用户 ID、查询 ID 的不同组合以及它是否为顶级语句(最多可跟踪的独立语句数量)包含一行。视图的列在表 F.22 中显示。
表 F.22. pg_stat_statements 列
列 类型 描述 |
---|
执行语句的用户 OID |
执行语句的数据库 OID |
如果查询被执行为顶级语句,则为真(如果 pg_stat_statements.track 设置为 top,则始终为真) |
用于识别相同标准化查询的哈希码。 |
代表性语句的文本 |
语句被规划的次数(如果 pg_stat_statements.track_planning 已启用,否则为零) |
规划该语句所花费的总时间(以毫秒为单位)(如果 pg_stat_statements.track_planning 已启用,否则为零) |
规划该语句所花费的最短时间(以毫秒为单位)。如果 pg_stat_statements.track_planning 被禁用,或者计数器使用 minmax_only 参数设置为 true 的 pg_stat_statements_reset 函数重置且自重置以来从未被规划过,则此字段将为零。 |
规划该语句所花费的最长时间(以毫秒为单位)。如果 pg_stat_statements.track_planning 被禁用,或者计数器使用 minmax_only 参数设置为 true 的 pg_stat_statements_reset 函数重置且自重置以来从未被规划过,则此字段将为零。 |
规划该语句所花费的平均时间(以毫秒为单位)(如果 pg_stat_statements.track_planning 已启用,否则为零) |
规划该语句所花费时间的总体标准偏差(以毫秒为单位)(如果 pg_stat_statements.track_planning 已启用,否则为零) |
语句执行的次数 |
执行该语句所花费的总时间(以毫秒为单位) |
执行该语句所花费的最短时间(以毫秒为单位),此字段将为零,直到该语句在执行了使用 minmax_only 参数设置为 true 的 pg_stat_statements_reset 函数执行重置后首次执行。 |
执行该语句所花费的最长时间(以毫秒为单位),此字段将为零,直到该语句在执行了使用 minmax_only 参数设置为 true 的 pg_stat_statements_reset 函数执行重置后首次执行。 |
执行该语句所花费的平均时间(以毫秒为单位) |
执行该语句所花费时间的总体标准偏差(以毫秒为单位) |
由该语句检索或受影响的行总数 |
该语句共享块缓存命中的总数 |
该语句读取的共享块总数 |
该语句弄脏的共享块总数 |
该语句写入的共享块总数 |
该语句本地块缓存命中的总数 |
该语句读取的本地块总数 |
该语句弄脏的本地块总数 |
该语句写入的本地块总数 |
该语句读取的临时块总数 |
该语句写入的临时块总数 |
该语句读取共享块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句写入共享块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句读取本地块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句写入本地块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句读取临时文件块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句写入临时文件块的总时间(以毫秒为单位)(如果 track_io_timing 已启用,否则为零) |
该语句生成的 WAL 记录总数 |
该语句生成的 WAL 全页映像总数 |
该语句生成的 WAL 总量(以字节为单位) |
WAL 缓冲区变满的次数 |
该语句 JIT 编译的函数总数 |
该语句用于生成 JIT 代码的总时间(以毫秒为单位) |
函数被内联的次数 |
该语句用于内联函数总时间(以毫秒为单位) |
该语句被优化的次数 |
该语句用于优化总时间(以毫秒为单位) |
代码被发出的次数 |
该语句用于发出代码的总时间(以毫秒为单位) |
该语句 JIT 编译的元组变形函数总数 |
该语句用于 JIT 编译元组变形函数总时间(以毫秒为单位) |
计划启动的并行工作进程数 |
实际启动的并行工作进程数 |
此语句开始收集统计信息的时间 |
此语句开始收集最小/最大统计信息的时间(字段 min_plan_time、max_plan_time、min_exec_time 和 max_exec_time) |
出于安全原因,只有超级用户和具有 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。pg_stat_statements_info 视图(下面在 F.32.2. pg_stat_statements_info 视图中讨论)提供了关于条目去分配的统计信息。
在某些情况下,具有明显不同文本的查询可能会合并到一个 pg_stat_statements 条目中;如上所述,这对于语义等效的查询是预期发生的。此外,如果查询之间唯一的区别是常量列表的元素数量,则该列表将被压缩成一个元素,但会显示一个带注释的列表指示符
=# SELECT pg_stat_statements_reset(); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7, 8); =# SELECT query, calls FROM pg_stat_statements WHERE query LIKE 'SELECT%'; -[ RECORD 1 ]------------------------------ query | SELECT * FROM test WHERE a IN ($1 /*, ... */) calls | 2
除了这些情况之外,还存在哈希冲突导致不相关查询合并成一个条目的可能性。(然而,这不会发生在属于不同用户或数据库的查询之间。)
由于 queryid 哈希值是在查询的 post-parse-analysis 表示形式上计算的,因此反过来也是可能的:具有相同文本的查询可能显示为不同的条目,如果它们由于 search_path 设置不同等因素而具有不同的含义。
pg_stat_statements 的使用者可能希望使用 queryid(可能与 dbid 和 userid 结合使用)作为每个条目比其查询文本更稳定可靠的标识符。但是,重要的是要理解 queryid 哈希值的稳定性仅有有限的保证。由于标识符是从 post-parse-analysis 树派生的,因此其值是诸如此表示形式中出现的内部对象标识符等的函数。这会产生一些令人费解的影响。例如,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 视图中的所有现有条目将显示 null 的 query 字段,但与每个 queryid 相关的统计信息将保留。如果发生这种情况,请考虑减小 pg_stat_statements.max 以防止再次发生。
plans 和 calls 并不总是如预期那样匹配,因为规划和执行统计信息是在各自的结束阶段更新的,并且仅针对成功操作。例如,如果一个语句成功规划但执行阶段失败,则仅更新其规划统计信息。如果由于使用了缓存的计划而跳过了规划,则仅更新其执行统计信息。
pg_stat_statements 模块本身的统计信息通过一个名为 pg_stat_statements_info 的视图进行跟踪和提供。此视图仅包含一行。视图的列在表 F.23 中显示。
表 F.23. pg_stat_statements_info 列
列 类型 描述 |
---|
由于观察到的独立语句数量超过 pg_stat_statements.max,最少执行语句的 pg_stat_statements 条目被去分配的总次数 |
pg_stat_statements 视图中所有统计信息上次重置的时间。 |
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 参数将返回 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 <itagaki.takahiro@oss.ntt.co.jp>
。查询标准化由 Peter Geoghegan <peter@2ndquadrant.com>
添加。
如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用此表单报告文档问题。