排查查询性能下降问题

本页面介绍如何检测查询执行速度的变化、检查查询执行计划,以及如何在必要时为将来的查询指定不同索引。

使用 REST API 以 JSON 格式获取详细的查询性能剖析文件

默认情况下,当您执行查询时,API 仅返回语句结果。这是因为 queryMode 设置为 NORMAL。如需在查询结果中包含详细的执行统计信息,请将 queryMode 设置为 PROFILE。

创建会话

更新查询模式之前,请创建一个会话,该会话代表与 Spanner 数据库服务之间的通信通道。

  1. 前往 projects.instances.databases.sessions.create 页面。
  2. 按以下形式提供项目 ID、实例 ID 和数据库 ID:

    projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID

  3. 点击 Execute。响应会显示您创建的会话,形式如下:

    projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID/sessions/SESSION_ID

    在下一步中,使用此会话对查询进行性能剖析。在两次连续使用之间,会话最多可保持活跃一小时,随后 Spanner 会将其删除。

如需详细了解如何使用会话,请参阅会话。

对查询进行性能剖析

为查询启用 PROFILE 模式。

  1. 前往 projects.instances.databases.sessions.executeSql 页面。
  2. 对于 session,输入您在创建会话中创建的会话 ID:

    projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID/sessions/SESSION_ID

  3. 对于 Request body,使用以下内容:

    { "sql": "SQL_QUERY", "queryMode": "PROFILE" }

  4. 点击 Execute。响应包括查询结果、查询计划和查询的执行统计信息。

排查索引使用问题

使用 SQL 查询查找数据时,Spanner 会自动使用可能更高效地检索数据的任何二级索引。不过,在少数情况下,Spanner 有可能选择会导致查询运行速度变慢的索引。因此,您可能会注意到一些查询的运行速度比预期慢。

检测查询执行速度的变化

进行了以下其中一项更改后,您最有可能会看到查询执行速度变化:

  • 对具有二级索引的大量现有数据进行重大更改。
  • 添加、更改或删除二级索引。

您可以使用多种不同的工具来确定 Spanner 执行速度比平常慢的特定查询:

新创建的数据库的优化器统计信息

查询包含新插入或导入数据的新创建的数据库时,Spanner 可能无法选择最合适的索引,因为查询优化器最多需要三天时间才能自动收集统计信息。如需更快地优化新 Spanner 数据库的索引用量,您可以手动构建新的统计信息软件包。

查看架构

找到运行速度变慢的查询后,请查看该查询的 SQL 语句,确定该语句使用的表以及它从这些表中检索的列。

接下来,查找这些表的二级索引。确定是否有任何索引包含您正在查询的列,这表示 Spanner 可能会使用其中一个索引来处理查询。

  • 如果存在适用索引,下一步是查找 Spanner 用于查询的索引。
  • 如果不存在适用索引,使用 gcloud spanner operations list 命令检查您近期是否删除了适用索引:

    gcloud spanner operations list \
        --instance=INSTANCE \
        --database=DATABASE \
        --filter="@TYPE:UpdateDatabaseDdlMetadata"
    

    如果您删除了某个相应索引,该更改可能已影响查询性能。将二级索引添加回表中。在 Spanner 添加索引后,再次运行查询并查看其性能。如果性能没有提高,下一步是查找 Spanner 用于查询的索引。

    如果您没有删除相应索引,则索引选择不会导致查询性能衰退。查看您的数据或使用模式是否发生了可能影响性能的其他更改。

查找用于查询的索引

如需了解 Spanner 使用哪个索引来处理查询,请在 Google Cloud 控制台中查看查询执行计划:

  1. 前往Google Cloud 控制台中的 Spanner 实例页面。

    转到“实例”页面

  2. 点击要查询的实例的名称。

  3. 在导航菜单中,选择要查询的数据库,然后点击 Spanner Studio。

  4. 输入要测试的查询。

  5. 在运行查询菜单中,选择仅说明。 Spanner 会显示查询计划。

在查询计划中查找以下至少一个运算符:

  • 表扫描
  • 索引扫描
  • 交叉应用或分布式交叉应用

以下部分介绍每个运算符的含义。

表扫描运算符

表扫描运算符表示 Spanner 未使用二级索引:

查询计划中的表扫描运算符。

例如,假设 Albums 表没有任何二级索引,您运行以下查询:

SELECT AlbumTitle FROM Albums WHERE STARTS_WITH(AlbumTitle, "Now");

由于没有要使用的索引,因此查询计划包括表扫描运算符。

索引扫描运算符

索引扫描运算符表示 Spanner 在处理查询时使用了二级索引:

查询计划中的索引扫描运算符。

例如,假设您向 Albums 表添加索引:

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

然后,运行以下查询:

SELECT AlbumTitle FROM Albums WHERE STARTS_WITH(AlbumTitle, "Now");

AlbumsByAlbumTitle 索引包含 AlbumTitle,这是查询选择的唯一列。因此,查询计划包括索引扫描运算符。

交叉应用运算符

在某些情况下,Spanner 使用的索引仅包含查询所选择的部分列。因此,Spanner 必须将索引与基表联接。

发生此类型联接时,查询计划包括具有以下输入内容的交叉应用或分布式交叉应用运算符:

  • 表索引的索引扫描运算符
  • 拥有索引的表的表扫描运算符

查询计划中的分布式交叉应用,其中包含索引扫描和表扫描作为输入内容。

例如,假设您向 Albums 表添加索引:

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

然后,运行以下查询:

SELECT * FROM Albums WHERE STARTS_WITH(AlbumTitle, "Now");

AlbumsByAlbumTitle 索引包含 AlbumTitle,但查询选择表中的所有列,而不只是 AlbumTitle。因此,查询计划包括分布式交叉应用运算符,并包含 AlbumsByAlbumTitle 的索引扫描和 Albums 的表扫描作为输入内容。

选择其他索引

找到 Spanner 用于您的查询的索引后,请尝试使用不同的索引来运行查询,或通过扫描基表运行查询,而不是使用索引。如需指定索引,向查询添加 FORCE_INDEX 指令。

如果您发现更快的查询版本,请更新应用以使用该较快的版本。

索引选择准则

请按照以下准则确定要针对查询测试的索引:

  • 如果您的查询满足以下任何条件,请尝试使用基表来代替二级索引:

    • 查询通过基表主键的前缀来检查是否相等(例如,SELECT * FROM Albums WHERE SingerId = 1)。
    • 有许多行满足查询谓词(例如,SELECT * FROM Albums WHERE AlbumTitle != "There Is No Album With This Title")。
    • 查询使用仅包含几百行的基表。
  • 如果查询包含选择性极强的谓词(例如,REGEXP_CONTAINS、STARTS_WITH、<、<=、>、>= 或 !=),请尝试使用所含列与您在谓词中使用的列相同的索引。

测试更新后的查询

使用 Google Cloud 控制台测试更新后的查询,并了解处理查询需要多长时间。

如果您的查询包括查询参数,并且某个查询参数与一些值的绑定比其他值更频繁,则在您的测试中将该查询参数与其中某个值绑定。例如,如果查询包括 WHERE country = @countryId 等谓词,并且您的几乎所有查询都将 @countryId 绑定到值 US,则在性能测试中将 @countryId 绑定到 US。此方法可帮助您针对自己最常运行的查询进行优化。

如需在 Google Cloud 控制台中测试更新后的查询,请执行以下步骤:

  1. 前往Google Cloud 控制台中的 Spanner 实例页面。

    转到“实例”页面

  2. 点击要查询的实例的名称。

  3. 在导航菜单中,点击您要查询的数据库,然后点击 Spanner Studio。

  4. 输入要测试的查询,包括 FORCE_INDEX 指令,然后点击运行查询。

    Google Cloud 控制台会打开结果表标签页,然后显示查询结果,其中包含 Spanner 服务处理查询所需的时间。

    该指标不包括其他延迟来源,例如, Google Cloud 控制台解读和显示查询结果所需的时间。

查询限制

在查询执行期间,Spanner 可以在多个位置限制查询。

客户端等待时间和服务器队列延迟时间是两个主要的限制指标。

客户端等待时间

客户端等待时间表示 Spanner 客户端等待应用使用结果的时间。这表示客户端处理结果的速度低于 Spanner 生成结果的速度。如需解决此问题,请提高使用方应用的性能,或在客户端应用中缓冲所有结果,并在查询完成后处理这些结果。

服务器队列延迟

当处理服务器延迟安排查询执行时,会发生服务器队列延迟。在查询摘要窗格中,查看服务器队列延迟时间指标;如果该指标不存在,则表示未发生服务器延迟。

此延迟可能表示执行查询的服务器过载。如需验证是否存在热点,请参阅查找数据库中的热点。如需排查热点问题,请参阅使用热分块统计信息排查热点问题。

最近更改的数据

对表数据的更改可能会影响查询性能。

过时的统计信息软件包

如果优化器使用过时的统计信息软件包,则可能会选择次优方案。在查询摘要部分中,您可以查看统计信息包。如果软件包过时或缺失,请运行 ANALYZE,以确保优化器统计信息是最新的。

查询摘要中的统计信息软件包

如需了解详情,请参阅统计信息软件包。

表内容的变化

查询性能取决于查询处理的数据。解决查询性能问题的最佳方法取决于数据的变化方式:

  • 值分布中的偏斜:统计信息软件包会捕获此偏斜,但查询优化器只能在您在查询中使用字面量值时使用此信息。
  • 行数发生较大变化:评估随着表大小的增加,谓词是否能充分过滤行。
  • 值大小倾斜:值大小倾斜与高开销标量谓词(例如,对包含大字符串的 STRING 值执行 LIKE 谓词)结合使用时会出现问题。

如需更深入地了解查询执行情况,请使用 REST API。