使用查询说明

借助 Query Explain,您可以提交 Datastore 模式查询 发送到后端并接收有关后端查询执行的详细性能统计信息 。它的功能类似于许多关系型数据库系统中的 EXPLAIN ANALYZE 操作。

您可以使用 Datastore 模式客户端库发送查询解释请求。

查询解释结果可以帮助您了解查询是如何执行的,向您揭示低效问题,并指明可能存在服务器端瓶颈的位置。

查询解释:

  • 在规划阶段提供有用的数据洞见,以便您可以调整查询索引并提高效率。
  • 帮助您了解每次查询的费用和性能, 您可以快速迭代不同的查询句式, 并优化其使用方式

了解查询解释选项:默认和分析

您可以使用 default 选项或 analyze 选项。

使用默认选项时,查询解释会规划查询,但会跳过执行阶段;并返回规划阶段的信息。您可以用该选项来检查查询是否具有必要的索引,并了解查询使用了哪些索引。例如,这可以帮助您确认特定查询是否使用了某个复合索引,而不是交叉使用多个不同的索引。

使用分析选项时,查询解释不仅会规划查询,还会执行查询。此选项会返回上文提及的所有规划信息,以及来自查询执行运行时的统计信息;包括结算信息以及查询执行的系统级数据洞见。您可以使用 用于测试各种查询和索引配置 以缩短成本并缩短延迟时间

查询解释如何收费?

使用默认选项解释查询时,不创建索引或读取 任意时间。无论查询复杂程度如何,一次读取操作 。

如果您在使用查询解释时选择的是分析选项,系统会相应执行索引和读取操作,因此您需要照常为查询付费。没有任何 分析活动产生的额外费用,只是相应查询的正常费用 调用。

使用默认选项执行查询

您可以使用客户端库提交默认选项请求。

请注意,查询说明结果使用 Identity and Access Management 进行身份验证, 对常规查询操作使用相同的权限

Java

如需了解如何安装和使用适用于 Datastore 模式的客户端库,请参阅 Datastore 模式客户端库。 如需了解详情,请参阅 Datastore 模式 Java API 参考文档

如需向 Datastore 模式进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证


import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.Query;
import com.google.cloud.datastore.QueryResults;
import com.google.cloud.datastore.models.ExplainMetrics;
import com.google.cloud.datastore.models.ExplainOptions;
import com.google.cloud.datastore.models.PlanSummary;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class QueryProfileExplain {
  public static void invoke() throws Exception {
    // Instantiates a client
    Datastore datastore = DatastoreOptions.getDefaultInstance().getService();

    // Build the query
    Query<Entity> query = Query.newEntityQueryBuilder().setKind("Task").build();

    // Set the explain options to get back *only* the plan summary
    QueryResults<Entity> results = datastore.run(query, ExplainOptions.newBuilder().build());

    // Get the explain metrics
    Optional<ExplainMetrics> explainMetrics = results.getExplainMetrics();
    if (!explainMetrics.isPresent()) {
      throw new Exception("No explain metrics returned");
    }
    PlanSummary planSummary = explainMetrics.get().getPlanSummary();
    List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
    System.out.println("----- Indexes Used -----");
    indexesUsed.forEach(map -> map.forEach((key, val) -> System.out.println(key + ": " + val)));
  }
}

请参阅响应中的 indexes_used 字段,了解查询计划中使用的索引:

"indexes_used": [
        {"query_scope": "Collection Group", "properties": "(__name__ ASC)"},
]

有关该报告的详细信息,请参阅 报告参考

使用分析选项执行查询

您可以使用客户端库提交默认选项请求。

请注意,查询分析结果使用 Identity and Access Management (IAM) 进行身份验证, 对常规查询操作使用相同的权限

Java

如需了解如何安装和使用 Datastore 模式的客户端库,请参阅 Datastore 模式客户端库。 如需了解详情,请参阅 Datastore 模式 Java API 参考文档

如需向 Datastore 模式进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.Query;
import com.google.cloud.datastore.QueryResults;
import com.google.cloud.datastore.models.ExecutionStats;
import com.google.cloud.datastore.models.ExplainMetrics;
import com.google.cloud.datastore.models.ExplainOptions;
import com.google.cloud.datastore.models.PlanSummary;
import java.util.List;
import java.util.Map;

public class QueryProfileExplainAnalyze {
  public static void invoke() throws Exception {
    // Instantiates a client
    Datastore datastore = DatastoreOptions.getDefaultInstance().getService();

    // Build the query
    Query<Entity> query = Query.newEntityQueryBuilder().setKind("Task").build();

    // Set explain options with analzye = true to get back the query stats, plan info, and query
    // results
    QueryResults<Entity> results =
        datastore.run(query, ExplainOptions.newBuilder().setAnalyze(true).build());

    // Get the result set stats
    if (!results.getExplainMetrics().isPresent()) {
      throw new Exception("No explain metrics returned");
    }
    ExplainMetrics explainMetrics = results.getExplainMetrics().get();

    // Get the execution stats
    if (!explainMetrics.getExecutionStats().isPresent()) {
      throw new Exception("No execution stats returned");
    }

    ExecutionStats executionStats = explainMetrics.getExecutionStats().get();
    Map<String, Object> debugStats = executionStats.getDebugStats();
    System.out.println("----- Debug Stats -----");
    debugStats.forEach((key, val) -> System.out.println(key + ": " + val));
    System.out.println("----------");

    long resultsReturned = executionStats.getResultsReturned();
    System.out.println("Results returned: " + resultsReturned);

    // Get the plan summary
    PlanSummary planSummary = explainMetrics.getPlanSummary();
    List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
    System.out.println("----- Indexes Used -----");
    indexesUsed.forEach(map -> map.forEach((key, val) -> System.out.println(key + ": " + val)));

    if (!results.hasNext()) {
      throw new Exception("query yielded no results");
    }

    // Get the query results
    System.out.println("----- Query Results -----");
    while (results.hasNext()) {
      Entity entity = results.next();
      System.out.printf("Entity: %s%n", entity);
    }
  }
}

请参阅 executionStats 对象以查找查询性能分析信息,例如:

{
    "resultsReturned": "5",
    "executionDuration": "0.100718s",
    "readOperations": "5",
    "debugStats": {
               "index_entries_scanned": "95000",
               "documents_scanned": "5"
               "billing_details": {
                     "documents_billable": "5",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}

如需详细了解该报告,请参阅报告参考文档

解读结果并相应做出调整

以下示例场景按类型和制作国家/地区查询电影,并演示了如何优化查询使用的索引。

有关该报告的详细信息,请参阅 “查询说明”报告参考

接下来,我们以下面这个 SQL 查询为例来进行说明。

SELECT *
FROM movies
WHERE category = 'Romantic' AND country = 'USA';

如果我们使用“分析”选项,以下报告输出就会显示查询在单字段索引 (category ASC, __name__ ASC)(country ASC, __name__ ASC) 上运行。它会扫描 16, 500 个索引条目,但返回了 只有 1200 个文档。

// Output query planning info
"indexes_used": [
    {"query_scope": "Collection Group", "properties": "(category ASC, __name__ ASC)"},
    {"query_scope": "Collection Group", "properties": "(country ASC, __name__ ASC)"},
]

// Output query status
{
    "resultsReturned": "1200",
    "executionDuration": "0.118882s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "16500",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}

为了优化查询的执行性能,您可以创建一个完全覆盖查询需求的复合索引(category ASC、country ASC、__name__ ASC)。

再次以分析模式运行查询后,我们可以看到新创建的 此查询已选择索引,该查询的运行速度更快, 。

// Output query planning info
    "indexes_used": [
        {"query_scope": "Collection Group", "properties": "(category ASC, country ASC, __name__ ASC)"}
        ]

// Output query stats
{
    "resultsReturned": "1200",
    "executionDuration": "0.026139s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "1200",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}

后续步骤