Utilizzare Query Explain

Spiegazione query ti consente di inviare query in modalità Datastore al backend e ricevere in cambio statistiche dettagliate sul rendimento dell'esecuzione delle query sul backend. Funziona come l'operazione EXPLAIN ANALYZE in molti sistemi di database relazionali.

Puoi inviare richieste di spiegazione query utilizzando le librerie client in modalità Datastore.

I risultati di Spiegazione query ti aiutano a capire come vengono eseguite le query, mostrandoti le inefficienze e la posizione dei probabili colli di bottiglia lato server.

Spiegazione query:

  • Fornisce insight sulla fase di pianificazione in modo che tu possa modificare gli indici delle query e aumentare l'efficienza.
  • Ti aiuta a comprendere i costi e il rendimento per ogni query e ti consente di eseguire rapidamente l'iterazione di diversi pattern di query per ottimizzarne l'utilizzo.

Comprendere le opzioni di Spiegazione query: predefinita e analizza

Le operazioni di Spiegazione query possono essere eseguite utilizzando l'opzione predefinita o analizza.

Con l'opzione predefinita, Spiegazione query pianifica la query, ma salta la fase di esecuzione. Vengono restituite le informazioni sulla fase di pianificazione. Puoi utilizzarla per verificare che una query abbia gli indici necessari e per capire quali indici vengono utilizzati. In questo modo, ad esempio, puoi verificare che una determinata query utilizzi un indice composto anziché dover intersecare molti indici diversi.

Con l'opzione analizza, Spiegazione query pianifica ed esegue la query. Vengono restituite tutte le informazioni di pianificazione menzionate in precedenza, insieme alle statistiche del runtime di esecuzione della query. Sono incluse le informazioni di fatturazione e gli insight a livello di sistema sull'esecuzione della query. Puoi utilizzare questo strumento per testare varie configurazioni di query e indici per ottimizzarne il costo e la latenza.

Quanto costa Spiegazione query?

Quando una query viene spiegata con l'opzione predefinita, non vengono eseguite operazioni di indice o di lettura. Indipendentemente dalla complessità della query, viene addebitata un'operazione di lettura.

Quando una query viene spiegata con l'opzione analizza, vengono eseguite operazioni di indice e di lettura, quindi la query viene addebitata come di consueto. Non è previsto alcun costo aggiuntivo per l'attività di analisi, ma solo il costo abituale per l'esecuzione della query.

Eseguire una query con l'opzione predefinita

Puoi utilizzare una libreria client per inviare una richiesta con l'opzione predefinita.

Tieni presente che i risultati di Spiegazione query vengono autenticati con Identity and Access Management, utilizzando le stesse autorizzazioni per le normali operazioni di query.

Java

Per scoprire come installare e utilizzare la libreria client in modalità Datastore, consulta Librerie client in modalità Datastore. Per saperne di più, consulta la documentazione di riferimento dell'API in modalità JavaDatastore.

Per eseguire l'autenticazione in modalità Datastore, configura le credenziali predefinite dell'applicazione. Per saperne di più, consulta Configura l'autenticazione per un ambiente di sviluppo locale.


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)));
  }
}

Consulta il campo indexes_used nella risposta per scoprire gli indici utilizzati nel piano di query:

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

Per saperne di più sul report, consulta il riferimento del report.

Eseguire una query con l'opzione analizza

Puoi utilizzare una libreria client per inviare una richiesta con l'opzione predefinita.

Tieni presente che i risultati di Analisi query vengono autenticati con Identity and Access Management (IAM), utilizzando le stesse autorizzazioni per le normali operazioni di query.

Java

Per scoprire come installare e utilizzare la libreria client in modalità Datastore, consulta Librerie client in modalità Datastore. Per saperne di più, consulta la documentazione di riferimento dell'API in modalità JavaDatastore.

Per eseguire l'autenticazione in modalità Datastore, configura le credenziali predefinite dell'applicazione. Per saperne di più, consulta Configura l'autenticazione per un ambiente di sviluppo locale.

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);
    }
  }
}

Consulta l'oggetto executionStats per trovare informazioni sulla profilazione delle query, ad esempio:

{
    "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",
               }
    }
}

Per saperne di più sul report, consulta il riferimento del report.

Interpretare i risultati e apportare modifiche

Il seguente scenario di esempio esegue query sui film per genere e paese di produzione e mostra come ottimizzare gli indici utilizzati dalla query.

Per saperne di più sul report, consulta il riferimento del report Spiegazione query.

A titolo illustrativo, supponiamo l'equivalente di questa query SQL.

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

Se utilizziamo l'opzione analizza, l'output del report seguente mostra che la query viene eseguita sugli indici a campo singolo (category ASC, __name__ ASC) e (country ASC, __name__ ASC). Scansiona 16.500 voci di indice, ma restituisce solo 1200 documenti.

// 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",
               }
    }
}

Per ottimizzare il rendimento dell'esecuzione della query, puoi creare un indice composto completamente coperto (category ASC, country ASC, __name__ ASC).

Se eseguiamo di nuovo la query in modalità di analisi, possiamo vedere che l'indice appena creato è selezionato per questa query e che la query viene eseguita in modo molto più rapido ed efficiente.

// 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",
               }
    }
}

Passaggi successivi