Utilizza spiegazione query

Query Explain consente di inviare query in modalità Datastore al backend e di ricevere statistiche dettagliate sulle prestazioni dell'esecuzione delle query di backend. Funziona come l'operazione EXPLAIN ANALYZE in molti sistemi di database relazionali.

Puoi inviare richieste Query Explain utilizzando le librerie client in modalità Datastore.

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

Spiegazione della query:

  • Fornisce insight sulla fase di pianificazione per consentirti di modificare gli indici di query e aumentare l'efficienza.
  • Ti consente di comprendere costi e prestazioni per ogni singola query e di eseguire rapidamente l'iterazione in diversi pattern di query per ottimizzarne l'utilizzo.

Informazioni sulle opzioni di Query Explain: impostazione predefinita e analisi

Le operazioni Query Explain possono essere eseguite utilizzando l'opzione default o analyze.

Con l'opzione predefinita, Query Explain pianifica la query, ma salta la fase di esecuzione. Verranno restituite le informazioni sulla fase dello strumento di pianificazione. Puoi utilizzarlo per verificare che una query abbia gli indici necessari e capire quali indici vengono utilizzati. In questo modo puoi verificare, ad esempio, che una determinata query utilizzi un indice composto anziché che si intersechi con molti indici diversi.

Con l'opzione di analisi, Query Explain pianifica ed esegue la query. Questa operazione restituisce tutte le informazioni dello strumento di pianificazione menzionate in precedenza insieme alle statistiche relative al runtime di esecuzione della query. Verranno incluse le informazioni di fatturazione e gli insight a livello di sistema sull'esecuzione della query. Puoi utilizzare questi strumenti per testare varie configurazioni di query e indici al fine di ottimizzarne costi e latenza.

Quanto costa la query Explain?

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

Quando una query viene spiegata con l'opzione di analisi, vengono eseguite le operazioni di indicizzazione e lettura, quindi ti viene addebitato il costo della query come di consueto. Non è previsto alcun costo aggiuntivo per l'attività di analisi, ma solo il normale addebito per la query in esecuzione.

Esegui una query con l'opzione predefinita

Puoi utilizzare una libreria client per inviare una richiesta di opzione predefinita.

Tieni presente che la query spiega che i risultati 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 per la modalità Datastore, consulta Librerie client in modalità Datastore. Per maggiori informazioni, consulta la documentazione di riferimento dell'API Java per la modalità Datastore.

Per eseguire l'autenticazione in modalità Datastore, configura Credenziali predefinite dell'applicazione. Per maggiori informazioni, consulta Configurare 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)));
  }
}

Controlla il campo indexes_used nella risposta per scoprire di più sugli indici utilizzati nel piano di query:

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

Per ulteriori informazioni sul report, consulta la documentazione di riferimento del report.

Eseguire una query con l'opzione di analisi

Puoi utilizzare una libreria client per inviare una richiesta di opzione predefinita.

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

Java

Per scoprire come installare e utilizzare la libreria client per la modalità Datastore, consulta Librerie client in modalità Datastore. Per maggiori informazioni, consulta la documentazione di riferimento dell'API Java per la modalità Datastore.

Per eseguire l'autenticazione in modalità Datastore, configura Credenziali predefinite dell'applicazione. Per maggiori informazioni, consulta Configurare 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);
    }
  }
}

Visualizza l'oggetto executionStats per trovare informazioni sulla profilazione delle query come:

{
    "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 ulteriori informazioni sul report, consulta la documentazione di riferimento del report.

Interpretare i risultati e apportare modifiche

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

Per ulteriori informazioni sul report, consulta la Guida di riferimento al report Query Explain.

Ad esempio, supponiamo l'equivalente di questa query SQL.

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

Se utilizziamo l'opzione di analisi, il seguente output del report mostra la query viene eseguita su indici a campo singolo (category ASC, __name__ ASC) e (country ASC, __name__ ASC). Analizza 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 le prestazioni dell'esecuzione della query, puoi creare un indice composto completamente coperto (ASC di categoria, ASC del paese, ASC __name__).

Eseguendo 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 molto più velocemente ed in modo più 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