使用查詢說明

您可以使用「查詢說明」將 Datastore 模式查詢提交至後端,並取得後端查詢執行的詳細效能統計資料。這項作業的功能與許多關聯式資料庫系統中的 EXPLAIN ANALYZE 作業類似。

您可以使用 Datastore 模式用戶端程式庫傳送查詢說明要求。

查詢說明結果可協助您瞭解查詢的執行方式,顯示效率不彰之處,以及可能發生伺服器端瓶頸的位置。

查詢說明:

  • 提供規劃階段的洞察資訊,方便您調整查詢索引並提升效率。
  • 協助您瞭解每項查詢的費用和效能,並快速疊代不同查詢模式,進而最佳化查詢用量。

瞭解查詢說明選項:預設和分析

您可以使用預設選項或分析選項,執行查詢說明作業。

使用預設選項時,查詢說明會規劃查詢,但略過執行階段。系統會傳回規劃師階段資訊。您可以藉此確認查詢是否具備必要索引,並瞭解使用的索引。舉例來說,這有助於驗證特定查詢是否使用複合式索引,而非必須在許多不同索引中交集。

使用分析選項時,查詢說明功能會規劃及執行查詢。這會傳回先前提及的所有規劃工具資訊,以及查詢執行階段的統計資料。包括帳單資訊,以及查詢執行的系統層級洞察資料。您可以使用這項工具測試各種查詢和索引設定,以最佳化費用和延遲時間。

查詢說明的費用是多少?

使用預設選項說明查詢時,系統不會執行任何索引或讀取作業。無論查詢有多複雜,系統都會收取一次讀取作業的費用。

使用「分析」選項說明查詢時,系統會執行索引和讀取作業,因此您仍須支付查詢費用。分析活動不會產生額外費用,系統只會針對執行的查詢收取一般費用。

使用預設選項執行查詢

您可以使用用戶端程式庫提交預設選項要求。

請注意,查詢說明結果會使用與一般查詢作業相同的權限,透過 Identity and Access Management 進行驗證。

Java

如要瞭解如何安裝及使用 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 模式 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) 上執行。掃描 16500 個索引項目,但只傳回 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",
               }
    }
}

後續步驟