查询统计信息

Spanner 提供内置表,表中存储许多关于 CPU 使用率最高的查询和 DML 语句以及所有查询总计(包括变更数据流查询)的统计信息。

可用性

SPANNER_SYS 数据只能通过 SQL 接口获得;例如:

Spanner 提供的其他单次读取方法不支持 SPANNER_SYS

按查询分组的 CPU 使用率

下面的表跟踪特定时间段内 CPU 使用率最高的查询:

  • SPANNER_SYS.QUERY_STATS_TOP_MINUTE:1 分钟时间段内的查询
  • SPANNER_SYS.QUERY_STATS_TOP_10MINUTE:10 分钟时间段内的查询
  • SPANNER_SYS.QUERY_STATS_TOP_HOUR:1 小时时间段内的查询

这些表具有以下属性:

  • 每个表包含表名指定的非重叠时间段长度的数据。

  • 时间段基于时钟时间。1 分钟时间段结束于整点分钟,10 分钟时间段结束于整点 10 分钟(自当前小时数开始),1 小时时间段结束于整点小时数。

    例如,在上午 11:59:30,SQL 查询可用的最近时间段为:

    • 1 分钟:上午 11:58:00–11:58:59
    • 10 分钟:上午 11:40:00–11:49:59
    • 1 小时:上午 10:00:00–10:59:59
  • Spanner 按 SQL 查询的文本对统计信息进行分组。如果是使用查询参数的查询,则 Spanner 会将该查询的所有执行结果放在一行。如果是使用字符串字面量的查询,则只有查询文本完全相同时,Spanner 才会对统计信息进行分组;当查询文本不同时,每个查询都会显示为单独的行。对于批处理 DML,Spanner 通过在生成指纹之前删除重复的连续相同的语句来规范化批次。

  • 如果存在请求标记,则 FPRINT 是该请求标记的哈希值。否则,它是 TEXT 值的哈希值。对于分区 DML,FPRINT 始终是 TEXT 值的哈希值。

  • 每行包含 Spanner 在指定时间段内捕获的特定 SQL 查询的所有执行结果的统计信息。

  • 如果 Spanner 无法存储在某时间段内运行的所有查询,则系统会优先存储在指定时间段内 CPU 使用率最高的查询。

  • 跟踪的查询包括完成、失败或被用户取消的查询。

  • 统计信息的子集专门指正在运行但未完成的查询:

    • 所有未成功的查询的执行计数和平均延迟时间(以秒为单位)。

    • 超时查询的执行计数。

    • 用户取消或因网络连接问题失败的查询的执行次数。

  • 表中的所有列均可为 null。

查询统计信息支持分区 DML 统计信息,具有以下属性:

  • 每个成功的分区 DML 语句都严格计为 1 次执行。已失败、已取消或正在执行的分区 DML 语句的执行计数为零。

  • 系统不会跟踪分区 DML 的 ALL_FAILED_EXECUTION_COUNTALL_FAILED_AVG_LATENCY_SECONDSCANCELLED_OR_DISCONNECTED_EXECUTION_COUNTTIMED_OUT_EXECUTION_COUNT 统计信息。

  • 每个分区 DML 语句的统计信息可能会在不同的时间间隔内显示。SPANNER_SYS.QUERY_STATS_TOP_10MINUTESPANNER_SYS.QUERY_STATS_TOP_HOUR 会为 10 分钟和 1 小时内完成的分区 DML 语句提供汇总视图。如需查看时长超过 1 小时的语句的统计信息,请参阅查询示例

表架构

列名 类型 说明
INTERVAL_END TIMESTAMP 包括的查询执行时所处的时间段的结束时间。
REQUEST_TAG STRING 此查询操作的可选请求标记。如需详细了解如何使用标记,请参阅使用请求标记进行问题排查
QUERY_TYPE STRING 指示查询是 PARTITIONED_QUERY 还是 QUERYPARTITIONED_QUERY 是指包含从 PartitionQuery API 获取的 partitionToken 的查询,或分区 DML 语句。所有其他查询和 DML 语句均采用 QUERY 查询类型表示。
TEXT STRING SQL 查询文本,被截断至 64KB 左右。

具有相同标记字符串的多个查询的统计信息归为一行,其中 REQUEST_TAG 与该标记字符串匹配。此字段仅显示其中一个查询的文本,被截断至 64KB 左右。 对于批处理 DML,这组 SQL 语句会展平为一行,并使用英文分号分隔符串联。连续相同的 SQL 文本会在截断之前删除重复。
TEXT_TRUNCATED BOOL 查询文本是否被截断。
TEXT_FINGERPRINT INT64 REQUEST_TAG 值的哈希值(如果存在);否则,TEXT 值的哈希值。 对应于审核日志中的 query_fingerprint 字段
EXECUTION_COUNT INT64 Spanner 在时间段内查看查询的次数。
AVG_LATENCY_SECONDS FLOAT64 数据库中每个查询执行的平均时间长度(以秒为单位)。该平均值不包括结果集编码和传输时间以及开销。
AVG_ROWS FLOAT64 查询返回的平均行数。
AVG_BYTES FLOAT64 查询返回的数据字节数的平均值,不包括传输编码开销。
AVG_ROWS_SCANNED FLOAT64 查询扫描的平均行数,不包括已删除的值。
AVG_CPU_SECONDS FLOAT64 Spanner 在执行查询的所有操作上花费的平均 CPU 时间(以秒为单位)。
ALL_FAILED_EXECUTION_COUNT INT64 查询在间隔期间失败的次数。
ALL_FAILED_AVG_LATENCY_SECONDS FLOAT64 数据库中每个查询执行失败的平均时间长度(以秒为单位)。该平均值不包括结果集编码和传输时间以及开销。
CANCELLED_OR_DISCONNECTED_EXECUTION_COUNT INT64 在该时间间隔内,查询被用户取消或由于断开的网络连接而失败的次数。
TIMED_OUT_EXECUTION_COUNT INT64 查询在时间间隔内超时的次数。
AVG_BYTES_WRITTEN FLOAT64 语句写入的平均字节数。
AVG_ROWS_WRITTEN FLOAT64 语句修改的平均行数。
STATEMENT_COUNT INT64 汇总到此条目的语句的总和。对于常规查询和 DML,此值等于执行计数。对于批处理 DML,Spanner 会捕获批次中的语句数量。
RUN_IN_RW_TRANSACTION_EXECUTION_COUNT INT64 作为读写事务的一部分运行查询的次数。 此列有助于您确定是否可以通过将查询移至只读事务来避免锁争用。
LATENCY_DISTRIBUTION ARRAY<STRUCT>

查询执行时间的直方图。这些值以秒为单位。

该数组包含单个元素,且具有以下类型:
ARRAY<STRUCT<
  COUNT INT64,
  MEAN FLOAT64,
  SUM_OF_SQUARED_DEVIATION FLOAT64,
  NUM_FINITE_BUCKETS INT64,
  GROWTH_FACTOR FLOAT64,
  SCALE FLOAT64,
  BUCKET_COUNTS ARRAY<INT64>>>

如需详细了解这些值,请参阅分布

如需根据分布计算百分位延迟时间,请使用 SPANNER_SYS.DISTRIBUTION_PERCENTILE(distribution, n FLOAT64) 函数,该函数会返回估算的 n 个百分位。如需查看相关示例,请参阅查找查询的第 99 百分位延迟时间

如需了解详情,请参阅百分位和分布值指标

AVG_MEMORY_PEAK_USAGE_BYTES FLOAT64

在分布式查询执行期间,平均峰值内存用量(以字节为单位)。

使用此统计信息可确定哪些查询或表数据大小可能会遇到内存限制。

AVG_MEMORY_USAGE_PERCENTAGE FLOAT64

在分布式查询执行期间,所需的平均内存用量(以此查询允许的内存限制的百分比表示)。

此统计信息仅跟踪执行查询所需的内存。某些运营商使用额外的缓冲内存来提升性能。所使用的额外缓冲内存会显示在查询计划中,但不会用于计算 AVG_MEMORY_USAGE_PERCENTAGE,因为缓冲内存用于优化,并不是必需的。

您可以使用此统计信息来确定哪些查询即将达到内存用量限额,并且如果数据大小增加,则可能会失败。如需降低查询失败的风险,请参阅 SQL 最佳实践以优化这些查询,或将查询拆分为读取较少数据的部分。

AVG_QUERY_PLAN_CREATION_TIME_SECS FLOAT64

查询编译(包括查询运行时创建)所花费的平均 CPU 时间(以秒为单位)。

如果此列的值较高,请使用参数化查询

AVG_FILESYSTEM_DELAY_SECS FLOAT64

查询从文件系统读取或因输入/输出 (I/O) 而被阻塞的平均时间。

使用此统计信息可找出文件系统 I/O 可能导致的高延迟。如需缓解此问题,请添加索引,或向现有索引添加 STORING (GoogleSQL) 或 INCLUDE (PostgreSQL) 子句

AVG_REMOTE_SERVER_CALLS FLOAT64

查询已完成的远程服务器调用 (RPC) 的平均数量。

您可以使用此统计信息来确定扫描相同行数的不同查询是否具有截然不同的 RPC 数量。您可以为 RPC 值较高的查询添加索引,或者向现有索引添加 STORING (GoogleSQL) 或 INCLUDE (PostgreSQL) 子句,以提高查询性能。

AVG_ROWS_SPOOLED FLOAT64

查询语句写入临时磁盘(而非内存)的平均行数。

使用此统计信息可识别可能存在高延迟且内存开销较高且无法在内存中执行的查询。如需缓解此问题,请更改 JOIN 顺序,或添加提供所需 SORT索引

失败的查询的 EXECUTION_COUNTAVG_LATENCY_SECONDSLATENCY_DISTRIBUTION 包括因语法不正确而失败的查询或遇到短暂错误但在重试时成功的查询。这些统计信息不会跟踪失败和已取消的分区 DML 语句。

聚合统计信息

还有一些表跟踪 Spanner 在特定时间段内为其捕获统计信息的所有查询的汇总数据:

  • SPANNER_SYS.QUERY_STATS_TOTAL_MINUTE:1 分钟时间段内的查询
  • SPANNER_SYS.QUERY_STATS_TOTAL_10MINUTE:10 分钟时间段内的查询
  • SPANNER_SYS.QUERY_STATS_TOTAL_HOUR:1 小时时间段内的查询

这些表具有以下属性:

  • 每个表包含表名指定的非重叠时间段长度的数据。

  • 时间段基于时钟时间。1 分钟时间段结束于整点分钟,10 分钟时间段结束于整点 10 分钟(自当前小时数开始),1 小时时间段结束于整点小时数。

    例如,在上午 11:59:30,SQL 查询可用的最近时间段为:

    • 1 分钟:上午 11:58:00–11:58:59
    • 10 分钟:上午 11:40:00–11:49:59
    • 1 小时:上午 10:00:00–10:59:59
  • 每行包含在指定时间段内通过数据库执行的所有查询的聚合统计信息。每个时间间隔只能有一行,包括已完成的查询、失败的查询和用户取消的查询。

  • TOTAL 表中捕获的统计信息可能包含 Spanner 在 TOP 表中未捕获的查询。

  • 这些表中的某些列会在 Cloud Monitoring 中显示为指标。 这些意外公开的指标包括:

    • 查询执行次数
    • 查询失败
    • 查询延迟时间
    • 返回的行数
    • 扫描的行数
    • 返回的字节数
    • 查询 CPU 时间

    如需了解详情,请参阅 Spanner 指标

表架构

列名 类型 说明
INTERVAL_END TIMESTAMP 包括的查询执行时所处的时间段的结束时间。
EXECUTION_COUNT INT64 Spanner 在时间间隔内发现查询的次数。
AVG_LATENCY_SECONDS FLOAT64 数据库中每个查询执行的平均时间长度(以秒为单位)。该平均值不包括结果集编码和传输时间以及开销。
AVG_ROWS FLOAT64 查询返回的平均行数。
AVG_BYTES FLOAT64 查询返回的数据字节数的平均值,不包括传输编码开销。
AVG_ROWS_SCANNED FLOAT64 查询扫描的平均行数,不包括已删除的值。
AVG_CPU_SECONDS FLOAT64 Spanner 在执行查询的所有操作上花费的平均 CPU 时间(以秒为单位)。
ALL_FAILED_EXECUTION_COUNT INT64 查询在间隔期间失败的次数。
ALL_FAILED_AVG_LATENCY_SECONDS FLOAT64 数据库中每个查询执行失败的平均时间长度(以秒为单位)。该平均值不包括结果集编码和传输时间以及开销。
CANCELLED_OR_DISCONNECTED_EXECUTION_COUNT INT64 在该时间间隔内,查询被用户取消或由于断开的网络连接而失败的次数。
TIMED_OUT_EXECUTION_COUNT INT64 查询在时间间隔内超时的次数。
AVG_BYTES_WRITTEN FLOAT64 语句写入的平均字节数。
AVG_ROWS_WRITTEN FLOAT64 语句修改的平均行数。
RUN_IN_RW_TRANSACTION_EXECUTION_COUNT INT64 作为读写事务的一部分运行查询的次数。 此列有助于您确定是否可以通过将某些查询移至只读事务来避免锁争用。
LATENCY_DISTRIBUTION ARRAY<STRUCT>

各个查询的执行时间直方图。 这些值以秒为单位。

请按如下方式指定数组:
ARRAY<STRUCT<
  COUNT INT64,
  MEAN FLOAT64,
  SUM_OF_SQUARED_DEVIATION FLOAT64,
  NUM_FINITE_BUCKETS INT64,
  GROWTH_FACTOR FLOAT64,
  SCALE FLOAT64,
  BUCKET_COUNTS ARRAY<INT64>>>

如需详细了解这些值,请参阅分布

如需根据分布计算百分位延迟时间,请使用 SPANNER_SYS.DISTRIBUTION_PERCENTILE(distribution, n FLOAT64) 函数,该函数会返回估算的 n 个百分位。如需查看相关示例,请参阅查找查询的第 99 百分位延迟时间

如需了解详情,请参阅百分位和分布值指标

数据保留

Spanner 至少为每个表保留以下时间段内的数据:

  • SPANNER_SYS.QUERY_STATS_TOP_MINUTESPANNER_SYS.QUERY_STATS_TOTAL_MINUTE:前 6 个小时中的时间段。

  • SPANNER_SYS.QUERY_STATS_TOP_10MINUTESPANNER_SYS.QUERY_STATS_TOTAL_10MINUTE:前 4 天中的时间段。

  • SPANNER_SYS.QUERY_STATS_TOP_HOURSPANNER_SYS.QUERY_STATS_TOTAL_HOUR:前 30 天中的时间段。

示例查询

本部分提供了几个可检索查询统计信息的示例 SQL 语句。您可以使用客户端库Google Cloud CLIGoogle Cloud 控制台运行这些 SQL 语句。

列出指定时间段内每个查询的基本统计信息

以下查询可返回前一分钟内顶级查询的原始数据:

SELECT text,
       request_tag,
       interval_end,
       execution_count,
       avg_latency_seconds,
       avg_rows,
       avg_bytes,
       avg_rows_scanned,
       avg_cpu_seconds
FROM spanner_sys.query_stats_top_minute
ORDER BY interval_end DESC;

列出运行时间超过一小时的分区 DML 语句的统计信息

以下查询会返回过去几小时内前几大分区 DML 查询的执行次数和平均写入行数:

SELECT text,
       request_tag,
       interval_end,
       sum(execution_count) as execution_count
       sum(avg_rows_written*execution_count)/sum(execution_count) as avg_rows_written
FROM spanner_sys.query_stats_top_hour
WHERE starts_with(text, "UPDATE") AND query_type = "PARTITIONED_QUERY"
group by text,request_tag, query_type;

列出 CPU 使用率最高的查询

以下查询可返回前一小时 CPU 使用率最高的查询:

SELECT text,
       request_tag,
       execution_count AS count,
       avg_latency_seconds AS latency,
       avg_cpu_seconds AS cpu,
       execution_count * avg_cpu_seconds AS total_cpu
FROM spanner_sys.query_stats_top_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_hour)
ORDER BY total_cpu DESC;

查找指定时间段内的总执行次数

以下查询可返回在前一个整 1 分钟时间段内执行的查询总数:

SELECT interval_end,
       execution_count
FROM spanner_sys.query_stats_total_minute
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_minute);

查找查询的平均延迟时间

以下查询可返回特定查询的平均延迟时间信息:

SELECT avg_latency_seconds
FROM spanner_sys.query_stats_top_hour
WHERE text LIKE "SELECT x FROM table WHERE x=@foo;";

查找查询的第 99 百分位延迟时间

以下查询会返回过去 10 分钟内运行的所有查询的执行时间第 99 个百分位数:

SELECT interval_end, avg_latency_seconds, SPANNER_SYS.DISTRIBUTION_PERCENTILE(latency_distribution[OFFSET(0)], 99.0)
  AS percentile_latency
FROM spanner_sys.query_stats_total_10minute
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_total_10minute)
ORDER BY interval_end;

比较平均延迟时间与第 99 百分位延迟时间有助于识别可能的异常查询(执行时间较长)。

查找扫描最多数据的查询

要衡量某查询扫描的数据量,您可以使用该查询扫描的行数作为统计方式。以下查询可返回前一小时执行的查询所扫描的行数:

SELECT text,
       execution_count,
       avg_rows_scanned
FROM spanner_sys.query_stats_top_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_hour)
ORDER BY avg_rows_scanned DESC;

查找写入数据最多的语句

如需衡量查询修改的数据量,您可以使用 DML 写入的行数(或写入的字节数)。以下查询会返回前一小时内由 DML 语句写入的行数:

SELECT text,
       execution_count,
       avg_rows_written
FROM spanner_sys.query_stats_top_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_hour)
ORDER BY avg_rows_written DESC;

所有查询的 CPU 总使用时间

以下查询可返回前一小时使用的 CPU 小时数:

SELECT (avg_cpu_seconds * execution_count / 60 / 60)
  AS total_cpu_hours
FROM spanner_sys.query_stats_total_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_total_hour);

列出在给定时间段内失败的查询

以下查询返回原始数据,包括过去一分钟内热门查询的失败查询次数和平均延迟时间。这些统计信息不会跟踪失败和已取消的分区 DML 语句。

SELECT text,
       request_tag,
       interval_end,
       execution_count,
       all_failed_execution_count,
       all_failed_avg_latency_seconds,
       avg_latency_seconds,
       avg_rows,
       avg_bytes,
       avg_rows_scanned,
       avg_cpu_seconds
FROM spanner_sys.query_stats_top_minute
WHERE all_failed_execution_count > 0
ORDER BY interval_end;

查找指定时间段内的总错误次数

以下查询返回在最近 1 分钟间隔内未能执行的查询总数。这些统计信息不会跟踪失败和已取消的分区 DML 语句。

SELECT interval_end,
       all_failed_execution_count
FROM spanner_sys.query_stats_total_minute
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_minute)
ORDER BY interval_end;

列出超时最多的查询

以下查询可返回前一小时内超时数量最高的查询。

SELECT text,
       execution_count AS count,
       timed_out_execution_count AS timeout_count,
       avg_latency_seconds AS latency,
       avg_cpu_seconds AS cpu,
       execution_count * avg_cpu_seconds AS total_cpu
FROM spanner_sys.query_stats_top_hour
WHERE interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.query_stats_top_hour)
ORDER BY timed_out_execution_count DESC;

查找查询成功执行和失败执行的平均延迟时间

以下查询返回特定查询的组合平均等待时间,成功执行的平均等待时间和失败执行的平均等待时间。这些统计信息不会跟踪失败和已取消的分区 DML 语句。

SELECT avg_latency_seconds AS combined_avg_latency,
       all_failed_avg_latency_seconds AS failed_execution_latency,
       ( avg_latency_seconds * execution_count -
         all_failed_avg_latency_seconds * all_failed_execution_count
       ) / (
       execution_count - all_failed_execution_count ) AS success_execution_latency
FROM   spanner_sys.query_stats_top_hour
WHERE  text LIKE "select x from table where x=@foo;";

使用查询统计信息排查 CPU 使用率高或查询延迟较长的问题

当您需要调查 Spanner 数据库上的高 CPU 使用率,或者您只是尝试了解数据库中 CPU 密集型查询形状时,查询统计信息非常有用。检查使用大量数据库资源的查询可让 Spanner 用户有机会降低运营费用,并可能缩短常规系统延迟时间。

您可以使用 SQL 代码或 Query Insights 信息中心来调查数据库中存在问题的查询。以下主题介绍如何使用 SQL 代码调查此类查询。

以下示例侧重于 CPU 使用率,不过您可按照类似的步骤来排查高查询延迟问题,并查找具有最高延迟时间的查询。只需按延迟时间(而非 CPU 使用率)即可选择时间间隔和查询。

选择要调查的时间段

调查之初是查找您的应用开始出现高 CPU 使用率的时间。例如,如果问题是于 2020 年 7 月 24 日下午 5:00(世界协调时间)开始。

收集所选时间段内的查询统计信息

选择开始调查的时间段后,我们将查看在约该时间的 QUERY_STATS_TOTAL_10MINUTE 表中收集的统计信息。此查询的结果可能会显示在该时间段内 CPU 和其他查询统计信息在该时间段内的变化情况

以下查询可返回从 16:3017:30 (世界协调时间)(含)之间的聚合查询统计信息。我们在查询中使用 ROUND 来限制显示小数位数。

SELECT interval_end,
       execution_count AS count,
       ROUND(avg_latency_seconds,2) AS latency,
       ROUND(avg_rows,2) AS rows_returned,
       ROUND(avg_bytes,2) AS bytes,
       ROUND(avg_rows_scanned,2) AS rows_scanned,
       ROUND(avg_cpu_seconds,3) AS avg_cpu
FROM spanner_sys.query_stats_total_10minute
WHERE
  interval_end >= "2020-07-24T16:30:00Z"
  AND interval_end <= "2020-07-24T17:30:00Z"
ORDER BY interval_end;

运行该查询会产生以下结果。

interval_end 计数 延时 rows_returned 字节 rows_scanned avg_cpu
2020-07-24T16:30:00Z 6 0.06 5.00 536.00 16.67 0.035
2020-07-24T16:40:00Z 55 0.02 0.22 25.29 0.22 0.004
2020-07-24T16:50:00Z 102 0.02 0.30 33.35 0.30 0.004
2020-07-24T17:00:00Z 154 1.06 4.42 486.33 7792208.12 4.633
2020-07-24T17:10:00Z 94 0.02 1.68 106.84 1.68 0.006
2020-07-24T17:20:00Z 110 0.02 0.38 34.60 0.38 0.005
2020-07-24T17:30:00Z 47 0.02 0.23 24.96 0.23 0.004

在上表中,我们看到平均 CPU 时间(结果表中的 avg_cpu 列)在高亮显示的时间间隔(截止17:00)中是最高的。我们还发现平均扫描的行数更高这表示从 16:50 到 17:00 运行的查询价格更高。选择这个时间间隔,以便在下一步中进一步研究。

查找导致 CPU 使用率高的问题查询

选择要调查的时间间隔后,我们现在查询 QUERY_STATS_TOP_10MINUTE 表。此查询的结果有助于确定哪些查询会导致 CPU 使用率高。

SELECT text_fingerprint AS fingerprint,
       execution_count AS count,
       ROUND(avg_latency_seconds,2) AS latency,
       ROUND(avg_cpu_seconds,3) AS cpu,
       ROUND(execution_count * avg_cpu_seconds,3) AS total_cpu
FROM spanner_sys.query_stats_top_10MINUTE
WHERE
  interval_end = "2020-07-24T17:00:00Z"
ORDER BY total_cpu DESC;

运行此查询会生成以下结果。

指纹 计数 延时 CPU total_cpu
5505124206529314852 30 3.88 17.635 529.039
1697951036096498470 10 4.49 18.388 183.882
2295109096748351518 1 0.33 0.048 0.048
11618299167612903606 1 0.25 0.021 0.021
10302798842433860499 1 0.04 0.006 0.006
123771704548746223 1 0.04 0.006 0.006
4216063638051261350 1 0.04 0.006 0.006
3654744714919476398 1 0.04 0.006 0.006
2999453161628434990 1 0.04 0.006 0.006
823179738756093706 1 0.02 0.005 0.0056

结果表中突出显示的前 2 个查询是平均 CPU 和延迟时间的离群值,以及执行次数和总 CPU 数。调查这些结果中列出的第一个查询。

比较查询随时间的变化

通过缩小调查范围,我们可以将注意力集中到 QUERY_STATS_TOP_MINUTE 表。通过比较特定查询随时间的运行情况,我们可以查找返回的行数或字节数,扫描的行数与 CPU 或延迟时间增加之间的相关性。偏差可能表明数据中的非均匀性。持续扫描的大量行可能表明缺乏适当的索引或次优连接顺序。

通过运行以下过滤该查询的 text_fingerprint 的语句,来研究表现出最高平均 CPU 使用率和最高延迟的查询。

SELECT interval_end,
       ROUND(avg_latency_seconds,2) AS latency,
       avg_rows AS rows_returned,
       avg_bytes AS bytes_returned,
       avg_rows_scanned AS rows_scanned,
       ROUND(avg_cpu_seconds,3) AS cpu,
FROM spanner_sys.query_stats_top_minute
WHERE text_fingerprint = 5505124206529314852
ORDER BY interval_end DESC;

运行此查询会返回以下结果。

interval_end 延时 rows_returned bytes_returned rows_scanned CPU
2020-07-24T17:00:00Z 4.55 21 2365 30000000 19.255
2020-07-24T16:00:00Z 3.62 21 2365 30000000 17.255
2020-07-24T15:00:00Z 4.37 21 2365 30000000 18.350
2020-07-24T14:00:00Z 4.02 21 2365 30000000 17.748
2020-07-24T13:00:00Z 3.12 21 2365 30000000 16.380
2020-07-24T12:00:00Z 3.45 21 2365 30000000 15.476
2020-07-24T11:00:00Z 4.94 21 2365 30000000 22.611
2020-07-24T10:00:00Z 6.48 21 2365 30000000 21.265
2020-07-24T09:00:00Z 0.23 21 2365 5 0.040
2020-07-24T08:00:00Z 0.04 21 2365 5 0.021
2020-07-24T07:00:00Z 0.09 21 2365 5 0.030

检查上述结果,我们发现扫描的行数,使用的 CPU 和延迟时间都在上午 9:00 左右发生了显着变化。为了了解这些数字急剧增加的原因,我们将检查查询文本,并查看架构中的任何更改是否可能影响查询。

使用以下查询来检索要调查的查询的查询文本。

SELECT text,
       text_truncated
FROM spanner_sys.query_stats_top_hour
WHERE text_fingerprint = 5505124206529314852
LIMIT 1;

这会返回以下结果。

text text_truncated
从 o_custkey = 36901 的订单中选择 *;

通过检查返回的查询文本,我们意识到该查询会在名为 o_custkey 的字段中进行过滤。这是 orders 表上的非键列。发生这种情况时,该列中的索引将在上午 9 点左右删除。这解释了此查询的费用变化。我们可以重新添加该索引,如果该查询很少运行,请确定没有索引,并接受更高的读取费用。

到目前为止,我们的调查工作重点放在了成功完成的查询中,并发现了数据库性能下降的一些原因。下一步,我们将着重介绍失败的或已取消的查询,并介绍如何检查数据以获取更多洞见。

调查失败的查询

未完成的查询仍会在超时前、被取消前或失败之前消耗资源。Spanner 跟踪失败查询和成功查询的执行计数和消耗的资源。这些统计信息不会跟踪失败和已取消的分区 DML 语句。

如需检查失败的查询是否会影响系统利用率,我们首先可以检查相关时间间隔中有多少查询失败。

SELECT interval_end,
       all_failed_execution_count AS failed_count,
       all_failed_avg_latency_seconds AS latency
FROM spanner_sys.query_stats_total_minute
WHERE
  interval_end >= "2020-07-24T16:50:00Z"
  AND interval_end <= "2020-07-24T17:00:00Z"
ORDER BY interval_end;
interval_end failed_count 延时
2020-07-24T16:52:00Z 1 15.211391
2020-07-24T16:53:00Z 3 58.312232

在进一步调查时,我们可以利用以下查询找出最有可能失败的查询。

SELECT interval_end,
       text_fingerprint,
       execution_count,
       avg_latency_seconds AS avg_latency,
       all_failed_execution_count AS failed_count,
       all_failed_avg_latency_seconds AS failed_latency,
       cancelled_or_disconnected_execution_count AS cancel_count,
       timed_out_execution_count AS to_count
FROM spanner_sys.query_stats_top_minute
WHERE all_failed_execution_count > 0
ORDER BY interval_end;
interval_end text_fingerprint execution_count failed_count cancel_count to_count
2020-07-24T16:52:00Z 5505124206529314852 3 1 1 0
2020-07-24T16:53:00Z 1697951036096498470 2 1 1 0
2020-07-24T16:53:00Z 5505124206529314852 5 2 1 1

如上表所示,使用指纹 5505124206529314852 的查询在不同时间间隔内多次失败。如果遇到此类故障,那么比较成功和失败的运行延迟时间会比较有趣。

SELECT interval_end,
       avg_latency_seconds AS combined_avg_latency,
       all_failed_avg_latency_seconds AS failed_execution_latency,
       ( avg_latency_seconds * execution_count -
         all_failed_avg_latency_seconds * all_failed_execution_count
       ) / (
       execution_count - all_failed_execution_count ) AS success_execution_latency
FROM   spanner_sys.query_stats_top_hour
WHERE  text_fingerprint = 5505124206529314852;
interval_end combined_avg_latency failed_execution_latency success_execution_latency
2020-07-24T17:00:00Z 3.880420 13.830709 2.774832

应用最佳实践

确定了要优化的候选查询后,我们接下来可以查看查询配置文件,并尝试使用 SQL 最佳做法进行优化。

后续步骤