Azure Database for PostgreSQL 灵活服务器中的查询存储

查询存储是Azure Database for PostgreSQL灵活服务器中的一项功能,它提供了一种跟踪查询性能随时间推移的方法。 查询存储可帮助你快速找到运行时间最长且资源密集型最高的查询,从而简化了性能问题的故障排除。 查询存储自动捕获查询和运行时统计信息的历史记录,并保留它们以供查看。 它按时间切分数据,以便可以查看时间使用模式。 所有用户、数据库和查询的数据存储在Azure Database for PostgreSQL灵活服务器中命名azure_sys的数据库。

启用查询存储

查询存储无需额外付费即可使用。 这是一项选择加入功能,因此默认情况下不会在服务器上启用该功能。 可以为给定服务器上的所有数据库全局启用或禁用查询存储。 无法针对每个数据库单独启用或关闭它。

重要

不要在可突发定价层上启用查询存储,因为它会导致性能问题。

在 Azure 门户中启用查询存储

  1. 登录到Azure门户并选择Azure Database for PostgreSQL灵活服务器。
  2. 在菜单的“设置”部分选择“参数”。
  3. 搜索 pg_qs.query_capture_mode 参数。
  4. 将该值设置为 topall,具体取决于你是要跟踪顶级查询,还是也要跟踪嵌套查询(即在函数或过程中执行的查询),然后选择保存。 留出最多 20 分钟以便第一批数据保存到 azure_sys 数据库中。

启用查询存储等待采样

  1. 搜索 pgms_wait_sampling.query_capture_mode 参数。
  2. 将值设置为 all保存

查询存储中的信息

查询存储由两个存储组成:

  • 用于保存查询执行统计信息的运行时统计信息存储。
  • 用于保存等待统计信息的等待统计信息存储。

使用查询存储的常见方案包括:

  • 确定在给定时间窗口内执行查询的次数。
  • 比较时间窗口之间某个查询的平均执行时间,以查看显著的差异。
  • 标识过去几个小时内运行时间最长的查询。
  • 标识正在等待资源的前 N 个查询。
  • 了解对特定查询的等待性质。

为尽量减少空间使用量,运行时统计信息存储中的运行时执行统计信息在一个固定的、可配置的时间范围内聚合。 可以使用视图查询这些存储中的信息。

访问查询存储信息

Azure Database for PostgreSQL灵活服务器将查询存储数据存储在azure_sys数据库中。 以下查询返回有关查询存储记录的查询的信息:

SELECT * FROM  query_store.qs_view;

此查询返回有关等待统计信息的信息:

SELECT * FROM  query_store.pgms_wait_sampling_view;

查找等待查询

等待事件类型根据相似性将不同的等待事件分组到存储桶中。 查询存储 提供等待事件类型、具体的等待事件名称以及相关查询。 将此等待信息与查询运行时统计信息相关联时,可以更深入地了解哪些内容有助于查询性能特征。

下面是有关如何使用 查询存储 中的等待统计信息深入了解工作负荷的一些示例:

观测 Action
高锁定等待 检查受影响查询的查询文本,并确定目标实体。 在查询存储中查找经常执行并具有较高持续时间且正在修改同一实体的其他查询。 确定这些查询后,请考虑更改应用程序逻辑以提高并发性,或使用限制较少的隔离级别。
高缓冲 IO 等待 在查询存储中查找物理读取次数较高的查询。 如果它们与发生高 IO 等待的查询相匹配,请考虑启用 自主优化 功能,看看它是否会建议创建某些索引,从而减少这些查询的物理读次数。
高内存等待 在查询存储中查找消耗内存最多的查询。 这些查询可能会延迟受影响查询的进一步进度。

配置选项

启用查询存储时,它会将数据保存在聚合窗口中。 这些窗口的长度由 pg_qs.interval_length_minutes 参数确定,该参数默认为 15 分钟。 对于每个窗口,查询存储最多存储 500 个不同的查询。 区分每个查询的唯一性的属性是 user_id (执行查询的用户的标识符)、 db_id (查询所执行的上下文中的数据库的标识符)和 query_id (唯一标识所执行的查询的整数值)。 如果不同查询数在配置的间隔内达到 500 个,则查询存储会释放 5% 记录的查询,以腾出更多空间。 首先解除分配的查询是执行次数最少的查询。

若要配置查询存储参数,请使用以下选项:

参数 说明 默认 范围
pg_qs.interval_length_minutes 查询存储的捕获时间间隔(分钟)。 定义数据暂留的频率。 15 1 - 30
pg_qs.max_captured_queries 查询存储的最大查询数会保留在每个捕获间隔期间记录的所有查询。 500 100 - 500
pg_qs.max_plan_size 查询存储从查询计划文本中保存的最大字节数。 较长的计划会被截断。 7500 100 - 10000
pg_qs.max_query_text_length 查询存储可以保存的最大查询长度。 较长的查询将被截断。 6000 100 - 10000
pg_qs.parameters_capture_mode 是否以及何时捕获查询位置参数。 capture_parameterless_only capture_parameterless_onlycapture_first_sample
pg_qs.query_capture_mode 要跟踪的语句。 none nonetopall
pg_qs.retention_period_in_days 查询存储的保持期窗口(以天为单位)。 系统会自动删除更旧的数据。 7 1 - 30
pg_qs.store_query_plans 查询存储是否应保存查询计划。 off onoff
pg_qs.track_utility 查询存储是否必须跟踪实用工具命令。 on onoff

注释

如果更改 pg_qs.max_query_text_length 参数的值,则查询存储在你进行更改之前捕获的所有查询文本将继续使用相同的 query_idsql_query_text。 此行为可能会给人留下新值不会生效的印象,但对于查询存储之前未记录的查询,你会看到查询文本使用新配置的最大长度。 此行为是设计造成的,在 视图和函数中对此进行了说明。 如果执行 query_store.qs_reset,它将删除查询存储到现在的所有信息,包括它为每个查询 ID 捕获的文本。 如果再次执行其中任一查询,新配置的最大长度将应用于正在捕获的文本。

以下选项专用于等待统计信息:

参数 说明 默认 范围
pgms_wait_sampling.history_period 对等待事件进行采样的频率(毫秒)。 100 1 - 600000
pgms_wait_sampling.query_capture_mode pgms_wait_sampling 扩展必须跟踪哪些语句。 none noneall

注释

pg_qs.query_capture_mode 取代了 pgms_wait_sampling.query_capture_mode。 如果 pg_qs.query_capture_modenone,则 pgms_wait_sampling.query_capture_mode 设置不起作用。

使用 Azure 门户获取或设置参数的不同值。

视图和函数

可以使用 query_store 数据库的 azure_sys 架构中提供的视图和函数,查询查询存储中记录的信息并将其删除。 PostgreSQL 公共角色中的任何人都可使用这些视图来查看查询存储中的数据。 这些视图仅在 azure_sys 数据库中可用

查询是通过查看其结构并忽略语义上不重要的任何内容(如文本、常量、别名或大小写差异)来规范化的。

如果两个查询在语义上是相同的,那么即使它们对同一引用的列和表使用不同的别名,它们也会使用相同的 query_id 进行标识。 如果两个查询之间只有在它们中使用的文本值不同,则它们也会使用相同的 query_id 进行标识。 对于使用同一 query_id 标识的查询,其 sql_query_text 将属于自查询存储启动录制活动以来首先执行的查询,或自上次放弃持久化数据以来首先执行的查询,因为执行函数 query_store.qs_reset

查询规范化的工作原理

以下示例演示查询规范化的工作原理:

假设使用以下语句创建表:

create table tableOne (columnOne int, columnTwo int);

可以启用查询存储数据收集,一个或多个用户按此确切顺序运行以下查询:

select * from tableOne;
select columnOne, columnTwo from tableOne;
select columnOne as c1, columnTwo as c2 from tableOne as t1;
select columnOne as "column one", columnTwo as "column two" from tableOne as "table one";

以前的所有查询共享相同的查询 ID。 查询存储保留启用数据收集后运行的第一个查询的文本。 因此,文本为 select * from tableOne;.

以下一组查询在规范化后将与上一组查询不匹配,因为 WHERE 子句使它们在语义上不同:

select columnOne as c1, columnTwo as c2 from tableOne as t1 where columnOne = 1 and columnTwo = 1;
select * from tableOne where columnOne = -3 and columnTwo = -3;
select columnOne, columnTwo from tableOne where columnOne = '5' and columnTwo = '5';
select columnOne as "column one", columnTwo as "column two" from tableOne as "table one" where columnOne = 7 and columnTwo = 7;

但是,此最后一组中的所有查询共享相同的查询 ID。 标识它们的所有文本是批处理中第一个查询的文本: select columnOne as c1, columnTwo as c2 from tableOne as t1 where columnOne = 1 and columnTwo = 1;

最后,以下查询与上一批查询的查询 ID 不匹配。 以下列表中解释了它们不匹配的原因:

查询:

select columnTwo as c2, columnOne as c1 from tableOne as t1 where columnOne = 1 and columnTwo = 1;

不匹配的原因:列列表引用相同的两列(columnOne 和 ColumnTwo),但顺序是相反的。 顺序从上一批中的 columnOne, ColumnTwo 变为此次查询中的 ColumnTwo, columnOne

查询:

select * from tableOne where columnTwo = 25 and columnOne = 25;

不匹配的原因:对 WHERE 子句中的表达式求值的顺序进行反向计算。 顺序从上一批中的 columnOne = ? and ColumnTwo = ? 变为此次查询中的 ColumnTwo = ? and columnOne = ?

查询:

select abs(columnOne), columnTwo from tableOne where columnOne = 12 and columnTwo = 21;

不匹配的原因:列的列表中的第一个表达式不再是 columnOne,但函数 abs 针对 columnOne (abs(columnOne)) 计算,这在语义上不等效。

查询:

select columnOne as "column one", columnTwo as "column two" from tableOne as "table one" where columnOne = ceiling(16) and columnTwo = 16;

不匹配的原因:WHERE 子句中的第一个表达式不再计算 columnOne 与某个文本的相等性,但函数 ceiling 的结果针对文本计算,这在语义上不等效。

Views

query_store.qs_view

此视图返回查询存储保存在其支持表中的所有数据。 查询存储区当前仍在内存中为活动时间窗口记录的数据,在该时间窗口结束之前不会显示出来;只有当内存中的易失性数据被收集并持久保存到磁盘上的表中后,这些数据才可见。 此视图为每个不同的数据库 (db_id)、用户 (user_id) 和查询 (query_id) 返回不同的行。

名称 类型 参考 说明
runtime_stats_entry_id bigint runtime_stats_entries 表中的 ID。
user_id oid pg_authid.oid 执行此语句的用户的 OID。
db_id oid pg_database.oid 在其中执行此语句的数据库的 OID。
query_id bigint 根据此语句的分析树计算的内部哈希代码。
query_sql_text varchar(10000) 代表语句的文本。 具有相同结构的不同查询聚集在一起。 此文本是群集中第一个查询的文本。 最大查询文本长度的默认值为 6,000,可以使用查询存储参数 pg_qs.max_query_text_length对其进行修改。 如果查询的文本超过此最大值,则它会被截断为前 pg_qs.max_query_text_length 个字节。
plan_id bigint 与此查询对应的计划的 ID。
start_time 时间戳 查询按时间窗口聚合。 参数 pg_qs.interval_length_minutes 定义这些窗口的时间跨度(默认值为 15 分钟)。 此列对应于记录此条目的窗口的开始时间。
end_time 时间戳 与此条目的时间窗口相对应的结束时间。
calls bigint 此时间窗口内执行该查询的次数。 对于并行查询,每次执行的调用次数等于 1(对应驱动查询执行的后端进程),再加上为协同执行执行树并行分支而启动的每个后端工作进程各计 1。
total_time 双精度 总查询执行时间(以毫秒为单位)。
min_time 双精度 最短查询执行时间(以毫秒为单位)。
max_time 双精度 最长查询执行时间(以毫秒为单位)。
mean_time 双精度 平均查询执行时间(以毫秒为单位)。
stddev_time 双精度 查询执行时间的标准偏差(以毫秒为单位)。
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 双精度 该语句读取块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零)。
blk_write_time 双精度 该语句写入块所花费的总时间,以毫秒为单位(如果启用了 track_io_timing,否则为零)。
is_system_query 布尔 确定 user_id = 10 的角色 (azuresu) 是否执行了查询。 该用户具有超级用户权限,用于执行控制平面操作。 由于此服务是托管的 PaaS 服务,因此只有 Microsoft 是该超级用户角色的一部分。
query_type 文本消息 查询表示的操作的类型。 可能的值为 unknownselectupdateinsertdeletemergeutilitynothingundefined
search_path 文本消息 捕获查询时设置的 search_path 的值。
query_parameters 文本消息 JSON 对象的文本表示形式,其值传递给参数化查询的位置参数。 此列仅在两种情况下填充其值:1)对于非参数化查询。 2)对于参数化查询,当 pg_qs.parameters_capture_mode 设置为 capture_first_sample 时,如果查询存储可以在执行时提取查询参数的值。
parameters_capture_status 文本消息 查询表示的操作的类型。 可能的值为 succeeded(查询未参数化,或者该查询已参数化且其值已成功捕获)、disabled(查询已参数化,但参数未被捕获,因为 pg_qs.parameters_capture_mode 被设置为 capture_parameterless_only)、too_long_to_capture(查询已参数化,但参数未被捕获,因为将显示在此视图 query_parameters 列中的结果 JSON 的长度被认为过长,不适合由查询存储持久保存)、too_many_to_capture(查询已参数化,但参数未被捕获,因为参数总数被认为过多,不适合由查询存储持久保存)、serialization_failed(查询已参数化,但作为参数传递的至少一个值无法序列化为文本)。

query_store.query_texts_view

此视图返回查询存储中的查询文本数据。 每个不同的 query_sql_text 都有一行。

名称 类型 说明
query_text_id bigint query_texts 表的 ID
query_sql_text varchar(10000) 代表语句的文本。 具有相同结构的不同查询聚集在一起。 此文本是群集中第一个查询的文本。
query_type smallint 查询表示的操作的类型。 在 PostgreSQL <版本 = 14 中,可能的值是0(未知)、(选择)、 12 (更新)、(插入)、 3 (删除)、 45 (实用工具)、 6 (无)。 在 PostgreSQL >版本 = 15 中,可能的值是0(未知)、 1 (选择)、 2 (更新)、(插入)、 3 (删除)、 45 (合并)、(实用工具)、 67 (无)。

query_store.pgms_wait_sampling_view

此视图返回查询存储中的等待事件数据。 此视图为每个不同的数据库 (db_id)、用户 (user_id)、查询 (query_id) 和事件 (event) 返回不同的行。

名称 类型 参考 说明
start_time 时间戳 查询按时间窗口聚合。 参数 pg_qs.interval_length_minutes 定义这些窗口的时间跨度(默认值为 15 分钟)。 此列对应于记录此条目的窗口的开始时间。
end_time 时间戳 与此条目的时间窗口相对应的结束时间。
user_id oid pg_authid.oid 执行此语句的用户的对象标识符。
db_id oid pg_database.oid 在其中执行语句的数据库的对象标识符。
query_id bigint 根据此语句的分析树计算的内部哈希代码。
event_type 文本消息 后端正在等待的事件类型。
event 文本消息 后端当前正在等待的等待事件名称。
calls 整数 已捕获同一事件的次数。

注释

如需 event_type 视图中 eventquery_store.pgms_wait_sampling_view 列中可能值的列表,请参阅 pg_stat_activity 的官方文档,并查找提及同名的列的信息。

查询存储.查询计划视图

此视图返回已用于执行查询的查询计划。 每个不同的数据库 ID 和查询 ID 都有一行。 查询存储仅记录非实用工具查询的查询计划。

名称 类型 参考 说明
plan_id bigint EXPLAIN 生成的规范化查询计划的哈希值。 它采用规范化形式,因为它不包括计划节点的估计成本以及缓冲区的使用情况。
db_id oid pg_database.oid 在其中执行此语句的数据库的 OID。
query_id bigint 根据此语句的分析树计算的内部哈希代码。
plan_text varchar(10000) 在给定了 costs=false、buffers=false 且 format=text 的情况下该语句的执行计划。 与 EXPLAIN 生成的输出相同。

Functions

query_store.qs_reset

此函数放弃查询存储收集的所有统计信息。 它会放弃已关闭的时间窗口的统计信息,这些统计信息已保存到磁盘表上。 它还放弃当前时间窗口的统计信息,该统计信息仅存在于内存中。 只有服务器管理员角色 (azure_pg_admin) 的成员才能执行此函数。

query_store.staging_data_reset

此函数会丢弃查询存储在内存中收集的所有统计信息。 此数据尚未写入到磁盘上的表中,而这些表用于支持查询存储中所收集数据的持久保存。 只有服务器管理员角色 (azure_pg_admin) 的成员才能执行此函数。

只读模式

当 Azure Database for PostgreSQL 灵活服务器处于只读模式时,例如当 default_transaction_read_only 参数设置为 on,或者由于达到存储容量而自动启用只读模式时,查询存储不会捕获任何数据。

在具有 只读副本 的服务器上启用查询存储不会自动在任何只读副本上启用查询存储。 即使在任何只读副本上启用它,查询存储也不会记录在任何只读副本上执行的查询。 只读副本在只读模式下运行,直到将其提升为主副本。