E.27. lt_stat_statements

E.27.1. pg_stat_statements 视图
E.27.2. 函数
E.27.3. 配置参数
E.27.4. 样例输出

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

该模块必须通过在lightdb.conf中将lt_stat_statements添加到shared_preload_libraries来加载,因为它需要额外的共享内存。这意味着需要重新启动服务器才能添加或删除该模块。

当加载 lt_stat_statements 后,它会跨服务器上的所有数据库跟踪统计信息。要访问和操作这些统计信息,该模块提供了一个视图 pg_stat_statements 和实用函数 pg_stat_statements_resetpg_stat_statements 。这些不是全局可用的,但可以通过 CREATE EXTENSION lt_stat_statements,为特定数据库启用。

E.27.1.  pg_stat_statements 视图

该模块收集的统计信息通过名为 pg_stat_statements 的视图可用。该视图对于每个不同的数据库 ID、用户 ID 和查询 ID(最多追踪模块可跟踪的不同语句数量)包含一行。该视图的列显示在 Table E.42 中。

Table E.42.  pg_stat_statements

列类型

描述

userid oid (参考 pg_authid .oid)

执行该语句的用户的 OID

dbid oid (参考 pg_database .oid)

执行该语句的数据库的 OID

queryid bigint

内部哈希码,从语句的解析树计算得出

query text

代表性语句的文本

plans bigint

该语句被计划的次数(如果启用了 lt_stat_statements.track_planning,否则为零)

total_plan_time double precision

计划该语句的总时间,以毫秒为单位(如果启用了 lt_stat_statements.track_planning,否则为零)

min_plan_time double precision

计划该语句最小的时间,以毫秒为单位(如果启用了 lt_stat_statements.track_planning,否则为零)

max_plan_time double precision

计划该语句最大的时间,以毫秒为单位(如果启用了 lt_stat_statements.track_planning,否则为零)

mean_plan_time double precision

计划该语句平均时间,以毫秒为单位(如果启用了 lt_stat_statements.track_planning,否则为零)

stddev_plan_time double precision

计划该语句时间的总体标准偏差,以毫秒为单位(如果启用了 lt_stat_statements.track_planning,否则为零)

calls bigint

该语句被执行的次数

total_exec_time double precision

执行该语句的总时间,以毫秒为单位

min_exec_time double precision

执行该语句最小的时间,以毫秒为单位

max_exec_time double precision

执行该语句最大的时间,以毫秒为单位

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

该语句写入的临时块总数

blk_read_time double precision

该语句读取块的总时间,以毫秒为单位 (如果启用了 track_io_timing,否则为零)

blk_write_time double precision

该语句写入块的总时间,以毫秒为单位 (如果启用了 track_io_timing,否则为零)

wal_records bigint

该语句生成的WAL记录总数

wal_fpi bigint

该语句生成的WAL完整页映像总数

wal_bytes numeric

该语句生成的WAL总字节数


基于安全考虑,只有超级用户和pg_read_all_stats角色的成员才能查看其他用户执行的SQL文本和 queryid。但是,如果在用户数据库中安装了该视图,则其他用户可以查看统计信息。

可计划的查询(即SELECTINSERTUPDATEDELETE )根据内部哈希计算,当它们具有相同的查询结构时,会被合并成一个pg_stat_statements条目。 通常,如果两个查询在语义上是等价的,除了查询中出现的文字常量的值之外,它们将被视为相同。然而,对于实用程序命令(即所有其他命令),它们严格按照它们的文本查询字符串进行比较。

当一个常量的值已被忽略,以便将查询与其他查询匹配时,该常量将在pg_stat_statements中被参数符号(如$1)替换。 查询文本的其余部分是具有特定queryid哈希值的第一个查询的文本,与pg_stat_statements条目相关联。

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

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

pg_stat_statements的使用者可能希望使用queryid(或许结合dbid userid)作为每个条目的更稳定和可靠的标识符,而不是使用其查询文本。 然而,重要的是要理解在queryid 哈希值的稳定性方面只有有限的保证。由于该标识符是从后分析树导出的,因此其值是该表示中出现的内部对象标识符等因素的函数。这有一些反直觉的含义。例如,如果两个明显相同的查询引用了在两个查询执行之间被删除并重新创建的表,那么 lt_stat_statements将认为这两个查询是不同的。 哈希处理还对机器架构和平台的其他方面的差异敏感。此外,不能保证queryidLightDB的主要版本之间稳定。

一般而言,只有在底层服务器版本和目录元数据细节完全相同的情况下,才能假定queryid值是稳定和可比的。基于物理WAL重放的复制中参与的两个服务器可以预期具有相同的 queryid值,用于相同的查询。但是,逻辑复制方案不能保证在所有相关细节上使副本完全相同,因此queryid 将不是跨一组逻辑副本累积成本的有用标识符。如果有疑问,建议进行直接测试。

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

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

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

E.27.2. 函数

pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void

pg_stat_statements_reset将与指定的useriddbid queryid相对应的lt_stat_statements 收集到的统计数据丢弃。如果没有指定参数,则对于每个参数使用默认值0(无效),并将与其他参数匹配的统计数据重置。如果未指定任何参数或所有指定的参数均为0 (无效),则将丢弃所有统计信息。默认情况下,只有超级用户才能执行此函数。可以使用GRANT授予其他用户访问权限。

pg_stat_statements(showtext boolean) returns setof record

pg_stat_statements视图是以同名函数pg_stat_statements定义的。客户端可以直接调用 pg_stat_statements函数,并通过指定showtext := false来省略查询文本(也就是说,对应于视图的 query列的OUT 参数将返回null)。此功能旨在支持可能希望避免重复检索长度不确定的查询文本的外部工具。这样的工具可以自行缓存每个条目观察到的第一个查询文本,因为这就是lt_stat_statements 所做的全部工作,然后根据需要检索查询文本。由于服务器将查询文本存储在文件中,因此这种方法可能会减少重复检查pg_stat_statements 数据的物理I/O。

E.27.3. 配置参数

lt_stat_statements.max (integer)

lt_stat_statements.max是模块跟踪的语句的最大数量(即pg_stat_statements 视图中最大的行数)。如果观察到的不同语句超过该数量,则舍弃执行最少的语句的信息。默认值为5000。此参数只能在服务器启动时设置。

lt_stat_statements.track (enum)

lt_stat_statements.track控制模块计数的语句类型。 指定top以跟踪顶层语句(直接由客户端发出的语句),指定all以跟踪嵌套语句(例如在函数内部调用的语句),或指定none 以禁用语句统计信息收集。默认值为top。只有超级用户可以更改此设置。

lt_stat_statements.track_utility (boolean)

lt_stat_statements.track_utility控制模块是否跟踪实用程序命令。实用程序命令是除SELECTINSERTUPDATEDELETE之外的所有命令。默认值为on 。只有超级用户可以更改此设置。

lt_stat_statements.track_planning (boolean)

lt_stat_statements.track_planning参数控制模块是否跟踪规划操作及其持续时间。 启用此参数可能会带来明显的性能损失,特别是当具有相同查询结构的语句被许多并发连接执行时, 这些连接会竞争更新一个较少的pg_stat_statements条目。默认值为off。 只有超级用户可以更改此设置。

lt_stat_statements.save (布尔型)

lt_stat_statements.save指定是否在服务器关闭时保存语句统计信息。 如果设置为off,则在关闭服务器时不保存统计信息,也不会在服务器启动时重新加载。 默认值为on。 该参数只能在lightdb.conf文件或服务器命令行中设置。

该模块需要额外的共享内存,数量与lt_stat_statements.max成比例。 请注意,该内存在模块加载时被消耗,即使lt_stat_statements.track设置为none

这些参数必须在lightdb.conf中设置。 典型的用法可能是:

# lightdb.conf
shared_preload_libraries = 'lt_stat_statements'

lt_stat_statements.max = 10000
lt_stat_statements.track = all

E.27.4. 样例输出

bench=# SELECT pg_stat_statements_reset();

$ ltbench -i bench
$ ltbench -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 ltbench_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 ltbench_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 ltbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 4 ]---+--------------------------------------------------​------------------
query           | UPDATE ltbench_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 ltbench_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 ltbench_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 ltbench_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 ltbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------​------------------
query           | UPDATE ltbench_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 ltbench_accounts add primary key (aid)
calls           | 1
total_exec_time | 160.588563
rows            | 0
hit_percent     | 100.0000000000000000
-[ RECORD 5 ]---+--------------------------------------------------​------------------
query           | vacuum analyze ltbench_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     |