F.29. pg_stat_statements

pg_stat_statements模块提供一种跟踪执行统计服务器执行的所有SQL语句的手段。

该模块必须通过在postgresql.conf中添加pg_stat_statementsshared_preload_libraries来加载,因为它需要额外的共享内存。 这意味着添加或删除这个模块都需要重启服务器。

F.29.1. pg_stat_statements 视图

该模块收集的统计通过一个名为pg_stat_statements的系统视图使其可用。 这个模块为每个不同的查询、数据库ID和用户ID(取决于该模块可以追踪的不同语句的最大值) 包含一行。视图的字段显示在表 F-20中。

表 F-20. pg_stat_statements 字段

名字类型参考描述
useridoidpg_authid.oid执行该语句的用户的OID
dbidoidpg_database.oid执行该语句的数据库的OID
queryidbigint 内部哈希代码,从声明的分析树中计算而来
querytext 有代表性的语句的文本
callsbigint 执行的次数
total_timedouble precision 该语句花费的总时间,以毫秒计
rowsbigint 该语句恢复或影响的行的总数
shared_blks_hitbigint 该语句命中的共享块缓存的总数
shared_blks_readbigint 该语句读取的共享块的总数
shared_blks_dirtiedbigint 该语句弄脏的共享块的总数
shared_blks_writtenbigint 该语句写入的共享块的总数
local_blks_hitbigint 该语句命中的本地块缓存的总数
local_blks_readbigint 该语句读取的本地块的总数
local_blks_dirtiedbigint 该语句弄脏的本地块的总数
local_blks_writtenbigint 该语句写入的本地块的总数
temp_blks_readbigint 该语句读取的临时块的总数
temp_blks_writtenbigint 该语句写入的临时块的总数
blk_read_timedouble precision  该语句读取块花费的总时间,以毫秒计 (如果启用了track_io_timing,否则为0)
blk_write_timedouble precision  该语句写入块花费的总时间,以毫秒计 (如果启用了track_io_timing,否则为0)

这个视图和函数pg_stat_statements_resetpg_stat_statements,只有在通过安装 pg_stat_statements扩展特别安装到的数据库中可用。 不过,当pg_stat_statements模块加载到服务器中时, 统计跟踪该服务器中的所有数据库,不管该视图是否存在。

为了安全起见,不允许非超级用户查看其它用户执行的查询的SQL文本或queryid。 不过,如果视图已经安装到他们的数据库中,那么他们可以看到统计。

可计划的查询(也就是,SELECT, INSERT, UPDATE, 和 DELETE)组合成为一个pg_stat_statements, 当它们根据一个内部哈希计算有相同的查询结构时。典型的,如果两个查询语义上相等, 除了查询中字面常量的值之外,我们认为这两个查询相同。工具命令(也就是,所有其他命令) 是直接基于它们的文本查询字符串比较的。

当一个常量的值为了匹配其他查询而忽略时,该常量在pg_stat_statements 的显示中被?替代。查询文本的剩余部分是第一个查询特定 queryid散列值与pg_stat_statements相关条目。

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

因为哈希值是基于分析查询的表示法之后来计算的,相反的也是可能的: 带有相同文本的查询可能表现为单独的条目,如果它们因为一个因素的结果有不同的含义, 比如不同的search_path设置。

pg_stat_statements的消耗者可能希望将queryid (可能和dbiduserid结合使用) 用作每个条目更稳定可靠的标识符而不是它的查询文本。 但是,重要的是要理解queryid哈希值的稳定性保证是有限的。 因为标识符来源于post-parse-analysis树, 它的值是在其他事物之上,出现在这个表示法中的内部对象标识符的一个函数。 这有一些违背常理。例如,如果在两个查询的执行期间它们引用的表删除之后又重新创建了, 那么pg_stat_statements将认为这两个看起来相同的查询是不同的。 哈希过程对机器架构和平台其他方面的不同也是敏感的。 另外,对queryidPostgreSQL 的主要版本还是稳定的这种假设是不安全的。

根据经验,只要底层服务器版本和目录元数据细节保持完全相同, queryid的值就可以假设为稳定和可比较的。 参与到基于物理WAL重放复制中的两个服务器,对于相同的查询可以预计为有不同的 queryid值。不过,逻辑复制模式并不保证复制品在所有相关细节上都相同, 所以queryid对于累加一系列逻辑复制的消耗来说不是一个有用的标识符。 如果有怀疑,建议直接做测试。

F.29.2. 函数

pg_stat_statements_reset() returns void

pg_stat_statements_reset抛弃所有pg_stat_statements 到目前为止收集的统计。缺省的,这个函数只能被超级用户执行。

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。

F.29.3. 配置参数

pg_stat_statements.max (integer)

pg_stat_statements.max是该模块追踪语句的最大值 (也就是,pg_stat_statements视图中的最大行数)。 如果观察了比这更多的不同的语句,则会抛弃执行最少的语句的信息。 缺省值是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, UPDATEDELETE的所有命令。缺省值是on。只有超级用户可以更改这个设置。

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'

pg_stat_statements.max = 10000
pg_stat_statements.track = all

F.29.4. 示例输出

bench=# SELECT pg_stat_statements_reset();

$ pgbench -i bench
$ pgbench -c10 -t300 bench

bench=# \x
bench=# SELECT query, calls, total_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_time DESC LIMIT 5;
-[ RECORD 1 ]---------------------------------------------------------------------
query       | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
calls       | 3000
total_time  | 9609.00100000002
rows        | 2836
hit_percent | 99.9778970000200936
-[ RECORD 2 ]---------------------------------------------------------------------
query       | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
calls       | 3000
total_time  | 8015.156
rows        | 2990
hit_percent | 99.9731126579631345
-[ RECORD 3 ]---------------------------------------------------------------------
query       | copy pgbench_accounts from stdin
calls       | 1
total_time  | 310.624
rows        | 100000
hit_percent | 0.30395136778115501520
-[ RECORD 4 ]---------------------------------------------------------------------
query       | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?;
calls       | 3000
total_time  | 271.741999999997
rows        | 3000
hit_percent | 93.7968855088209426
-[ RECORD 5 ]---------------------------------------------------------------------
query       | alter table pgbench_accounts add primary key (aid)
calls       | 1
total_time  | 81.42
rows        | 0
hit_percent | 34.4947735191637631

F.29.5. 作者

Takahiro Itagaki 。 Peter Geoghegan 添加了查询正常化。