你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
使用查询存储来监视性能
适用于: Azure Database for PostgreSQL 灵活服务器
Azure Database for PostgreSQL 灵活服务器中的查询存储功能提供了一种跟踪一段时间内的查询性能的方法。 通过帮助快速查找运行时间最长且资源最密集的查询,查询存储可简化性能问题的故障排除。 查询存储自动捕获查询和运行时统计信息的历史记录,并保留它们以供查看。 它按时间切分数据,以便可以查看时间使用模式。 所有用户、数据库和查询的数据都存储在 Azure Database for PostgreSQL 灵活服务器实例中名为 azure_sys
的数据库中。
启用查询存储
查询存储无需额外收费便可使用。 它是一项选择加入功能,因此默认情况下它未在服务器上启用。 对于给定服务器上的所有数据库,可以全局启用或禁用查询存储,但无法针对每个数据库启用或禁用。
重要
不要在可突发定价层上启用查询存储,因为这会影响性能。
在 Azure 门户中启用查询存储
- 登录到 Azure 门户并选择你的 Azure Database for PostgreSQL 灵活服务器实例。
- 在菜单的“设置”部分中选择“服务器参数”。
- 搜索
pg_qs.query_capture_mode
参数。 - 将值设置为
top
或all
,具体取决于你是要跟踪顶级查询还是也要跟踪嵌套查询(在函数或过程中执行的查询),然后选择“保存”。 留出最多 20 分钟以便第一批数据保存到azure_sys
数据库中。
启用查询存储等待采样
- 搜索
pgms_wait_sampling.query_capture_mode
参数。 - 将值设置为
all
并保存。
查询存储中的信息
查询存储由两个存储组成:
- 用于保存查询执行统计信息的运行时统计信息存储。
- 用于保存等待统计信息的等待统计信息存储。
使用查询存储的常见方案包括:
- 确定在给定时间窗口内执行查询的次数。
- 比较时间窗口之间某个查询的平均执行时间,以查看显著的差异。
- 标识过去几个小时内运行时间最长的查询。
- 标识正在等待资源的前 N 个查询。
- 了解对特定查询的等待性质。
为尽量减少空间使用量,运行时统计信息存储中的运行时执行统计信息在一个固定的、可配置的时间范围内聚合。 可以使用视图查询这些存储中的信息。
访问查询存储信息
查询存储数据存储在 Azure Database for PostgreSQL 灵活服务器实例上的 azure_sys
数据库中。
以下查询返回有关查询存储中记录的查询的信息:
SELECT * FROM query_store.qs_view;
此查询返回有关等待统计信息的信息:
SELECT * FROM query_store.pgms_wait_sampling_view;
查找等待查询
等待事件类型按相似性将不同的等待事件组合到存储桶中。 查询存储提供等待事件类型、特定等待事件名称和有争议的查询。 能将此等待信息与查询运行时统计信息相关联,意味着可更深入地了解有助于查询性能特征的因素。
以下是一些示例,说明如何使用查询存储中的等待统计信息获得有关工作负载的更多见解:
观测 | 操作 |
---|---|
高锁定等待 | 检查受影响查询的查询文本,并确定目标实体。 在查询存储中查找频繁执行和/或具有较高持续时间并修改同一实体的其他查询。 确定这些查询后,请考虑更改应用程序逻辑以提高并发性,或使用限制较少的隔离级别。 |
高缓冲 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.is_enabled_fs |
仅供内部使用:此参数用作功能覆盖开关。 如果显示为关闭,则无论为 pg_qs.query_capture_mode 设置了什么值,查询存储都会被禁用。 |
on |
on 、off |
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_only 、capture_first_sample |
pg_qs.query_capture_mode |
要跟踪的语句。 | none |
none 、top 、all |
pg_qs.retention_period_in_days |
查询存储的保持期窗口(天)。 系统会自动删除更旧的数据。 | 7 |
1 - 30 |
pg_qs.store_query_plans |
是否应在查询存储中保存查询计划。 | off |
on 、off |
pg_qs.track_utility |
查询存储是否必须跟踪实用工具命令。 | on |
on 、off |
(*) 静态服务器参数,其值的更改需要服务器重启才能生效。
以下选项专用于等待统计信息:
参数 | 说明 | 默认 | 范围 |
---|---|---|---|
pgms_wait_sampling.history_period |
对等待事件进行采样的频率(毫秒)。 | 100 |
1 - 600000 |
pgms_wait_sampling.is_enabled_fs |
仅供内部使用:此参数用作功能覆盖开关。 如果显示为 off ,则无论为 pgms_wait_sampling.query_capture_mode 设置了什么值,等待采样都会被禁用。 |
on |
on 、off |
pgms_wait_sampling.query_capture_mode |
pgms_wait_sampling 扩展必须跟踪哪些语句。 |
none |
none 、all |
注意
pg_qs.query_capture_mode
取代了 pgms_wait_sampling.query_capture_mode
。 如果 pg_qs.query_capture_mode
为 none
,则 pgms_wait_sampling.query_capture_mode
设置不起作用。
使用 Azure 门户获取或设置参数的不同值。
视图和函数
可以使用 azure_sys
数据库的 query_store
架构中提供的一些视图和函数来查询由查询存储记录的信息,或删除该信息。 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";
上述所有查询共享同一 query_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;
但是,这最后一组中的所有查询都共享同一 query_id,并且用于标识它们的文本都是该批中第一个查询的文本 select columnOne as c1, columnTwo as c2 from tableOne as t1 where columnOne = 1 and columnTwo = 1;
。
最后,在下面找到一些与上一批中的查询的 query_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
的结果针对文本计算,这在语义上不等效。
视图
query_store.qs_view
此视图会返回查询存储的支持表中持久保存的所有数据。 在当前活动时间窗口内仍然记录在内存中的数据在时间窗口结束之前不可见,其内存中易失性数据会被收集和持久保存到存储在磁盘上的表。 此视图为每个不同的数据库 (db_id)、用户 (user_id) 和查询 (query_id) 返回不同的行。
Name | 类型 | 参考 | 描述 |
---|---|---|---|
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) | 代表语句的文本。 具有相同结构的不同查询聚集在一起;此文本是群集中第一个查询的文本。 最大查询文本长度的默认值为 6000,可以使用查询存储参数 pg_qs.max_query_text_length 进行修改。 如果查询的文本超过此最大值,则它会被截断为前 pg_qs.max_query_text_length 个字节。 |
|
plan_id |
bigint | 与此查询对应的计划的 ID。 | |
start_time |
timestamp | 查询按时间窗口聚合。 服务器参数 pg_qs.interval_length_minutes 定义这些窗口的时间跨度(默认值为 15 分钟)。 此列对应于记录此条目的窗口的开始时间。 |
|
end_time |
timestamp | 与此条目的时间窗口相对应的结束时间。 | |
calls |
bigint | 此时间窗口内执行该查询的次数。 请注意,对于并行查询,每个执行的调用数对应于驱动查询执行的后端进程的 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 |
boolean | 确定 user_id = 10 的角色 (azuresu) 是否执行了查询。 该用户具有超级用户权限,用于执行控制平面操作。 由于此服务是托管的 PaaS 服务,因此只有 Microsoft 是该超级用户角色的一部分。 | |
query_type |
text | 查询表示的操作的类型。 可能的值为 unknown 、select 、update 、insert 、delete 、merge 、utility 、nothing 、undefined 。 |
|
search_path |
text | 捕获查询时设置的 search_path 值。 | |
query_parameters |
text | JSON 对象的文本表示形式,其值传递给参数化查询的位置参数。 此列仅在两种情况下填充其值:1)对于非参数化查询。 2)对于参数化查询,当 pg_qs.parameters_capture_mode 设置为 capture_first_sample 时,如果查询存储可以在执行时提取查询参数的值。 |
|
parameters_capture_status |
text | 查询表示的操作的类型。 可能值为 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 都有一行。
Name | 类型 | 描述 |
---|---|---|
query_text_id |
bigint | query_texts 表的 ID |
query_sql_text |
varchar(10000) | 代表语句的文本。 具有相同结构的不同查询聚集在一起;此文本是群集中第一个查询的文本。 |
query_type |
smallint | 查询表示的操作的类型。 在 <= 14 的 PostgreSQL 版本中,可能的值为 0 (unknown)、1 (select)、2 (update)、3 (insert)、4 (delete)、5 (utility)、6 (nothing)。 在 >= 15 的 PostgreSQL 版本中,可能的值为 0 (unknown)、1 (select)、2 (update)、3 (insert)、4 (delete)、5 (merge)、6 (utility)、7 (nothing)。 |
query_store.pgms_wait_sampling_view
此视图返回查询存储中的等待事件数据。 此视图为每个不同的数据库 (db_id)、用户 (user_id)、查询 (query_id) 和事件 (event) 返回不同的行。
Name | 类型 | 参考 | 描述 |
---|---|---|---|
start_time |
timestamp | 查询按时间窗口聚合。 服务器参数 pg_qs.interval_length_minutes 定义这些窗口的时间跨度(默认值为 15 分钟)。 此列对应于记录此条目的窗口的开始时间。 |
|
end_time |
timestamp | 与此条目的时间窗口相对应的结束时间。 | |
user_id |
oid | pg_authid.oid | 执行此语句的用户的对象标识符。 |
db_id |
oid | pg_database.oid | 在其中执行语句的数据库的对象标识符。 |
query_id |
bigint | 根据此语句的分析树计算的内部哈希代码。 | |
event_type |
text | 后端正在等待的事件类型。 | |
event |
text | 后端当前正在等待的等待事件名称。 | |
calls |
integer | 同一事件被捕获的次数。 |
注意
如需 query_store.pgms_wait_sampling_view
视图中 event_type
和 event
列中可能值的列表,请参阅 pg_stat_activity 的官方文档,并查找提及同名的列的信息。
query_store.query_plans_view
此视图返回已用于执行查询的查询计划。 每个不同的数据库 ID 和查询 ID 都有一行。 查询存储仅记录非实用工具查询的查询计划。
Name | 类型 | 参考 | 描述 |
---|---|---|---|
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 生成的输出相同。 |
函数
query_store.qs_reset
此函数会丢弃查询存储到目前为止收集的所有统计信息。 它会放弃已关闭的时间窗口的统计信息,这些统计信息已保存到磁盘表上。 它还会放弃当前时间窗口的统计信息,该统计信息仅存在于内存中。 只有服务器管理员角色 (azure_pg_admin
) 的成员才能执行此函数。
query_store.staging_data_reset
此函数会丢弃查询存储在内存中收集的所有统计信息,即尚未刷新到支持为查询存储对收集的数据进行持久保存的磁盘表上的内存中数据。 只有服务器管理员角色 (azure_pg_admin
) 的成员才能执行此函数。
只读模式
当 Azure Database for PostgreSQL 灵活服务器实例处于只读模式(例如 default_transaction_read_only
参数设置为 on
时)或由于达到存储容量而自动启用只读模式时,查询存储不会捕获任何数据。
在具有只读副本的服务器上启用查询存储不会自动在任何只读副本上启用查询存储。 即使在任何只读副本上启用了它,查询存储也不会记录在任何只读副本上执行的查询,因为它们以只读模式运行,直到你将其提升为主要副本。
与 Azure Database for PostgreSQL 产品团队分享你的建议和 bug。