Use a explicação de consultas

A funcionalidade Query Explain permite-lhe enviar consultas do modo Datastore para o back-end e receber estatísticas de desempenho detalhadas sobre a execução de consultas do back-end em troca. Funciona como a operação EXPLAIN ANALYZE em muitos sistemas de bases de dados relacionais.

Pode enviar pedidos de explicação de consultas através das bibliotecas cliente do modo Datastore.

Os resultados da explicação da consulta ajudam a compreender como as consultas são executadas, mostrando ineficiências e a localização de prováveis gargalos do lado do servidor.

Explicação da consulta:

  • Fornece estatísticas sobre a fase de planeamento para que possa ajustar os índices de consultas e aumentar a eficiência.
  • Ajuda a compreender o custo e o desempenho com base em cada consulta e permite iterar rapidamente diferentes padrões de consulta para otimizar a respetiva utilização.

Compreenda as opções de explicação de consultas: predefinição e análise

As operações Query Explain podem ser realizadas através da opção predefinição ou da opção analisar.

Com a opção predefinida, o Query Explain planeia a consulta, mas ignora a fase de execução. Esta ação devolve informações da fase do planeador. Pode usar esta opção para verificar se uma consulta tem os índices necessários e compreender que índices são usados. Isto ajuda a verificar, por exemplo, se uma consulta específica está a usar um índice composto em vez de ter de fazer a interseção em muitos índices diferentes.

Com a opção de análise, o Query Explain planeia e executa a consulta. Isto devolve todas as informações do planeador mencionadas anteriormente, juntamente com as estatísticas do tempo de execução da consulta. Isto inclui informações de faturação, bem como estatísticas ao nível do sistema sobre a execução da consulta. Pode usar estas ferramentas para testar várias configurações de consultas e índices para otimizar o respetivo custo e latência.

Quanto custa o comando Query Explain?

Quando uma consulta é explicada com a opção predefinida, não são realizadas operações de índice nem de leitura. Independentemente da complexidade da consulta, é cobrada uma operação de leitura.

Quando uma consulta é explicada com a opção de análise, são realizadas operações de indexação e leitura, pelo que lhe é cobrado o valor da consulta como habitualmente. Não existe qualquer custo adicional para a atividade de análise, apenas o custo habitual da consulta que está a ser executada.

Executar uma consulta com a opção predefinida

Pode usar uma biblioteca cliente para enviar um pedido de opção predefinida.

Tenha em atenção que os resultados da explicação de consultas são autenticados com a gestão de identidade e de acesso, usando as mesmas autorizações para operações de consulta normais.

Java

Para saber como instalar e usar a biblioteca cliente para o modo Datastore, consulte o artigo Bibliotecas cliente do modo Datastore. Para mais informações, consulte a documentação de referência da API Java do modo Datastore.

Para autenticar no modo Datastore, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.


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

Consulte o campo indexes_used na resposta para saber mais sobre os índices usados no plano de consulta:

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

Para mais informações sobre o relatório, consulte a referência do relatório.

Execute uma consulta com a opção de análise

Pode usar uma biblioteca cliente para enviar um pedido de opção predefinida.

Tenha em atenção que os resultados da análise de consultas são autenticados com a gestão de identidade e de acesso (IAM), usando as mesmas autorizações para operações de consulta normais.

Java

Para saber como instalar e usar a biblioteca cliente para o modo Datastore, consulte o artigo Bibliotecas cliente do modo Datastore. Para mais informações, consulte a documentação de referência da API Java do modo Datastore.

Para autenticar no modo Datastore, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.

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

Consulte o objeto executionStats para encontrar informações de criação de perfis de consultas, como:

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

Para mais informações sobre o relatório, consulte a referência do relatório.

Interprete os resultados e faça ajustes

O cenário de exemplo seguinte consulta filmes por género e país de produção, e demonstra como otimizar os índices usados pela consulta.

Para mais informações sobre o relatório, consulte a referência do relatório de explicação de consultas.

Para ilustração, suponha o equivalente a esta consulta SQL.

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

Se usarmos a opção de análise, o resultado do relatório seguinte mostra que a consulta é executada em índices de campo único (category ASC, __name__ ASC) e (country ASC, __name__ ASC). Analisa 16 500 entradas de índice, mas devolve apenas 1200 documentos.

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

Para otimizar o desempenho da execução da consulta, pode criar um índice composto totalmente coberto (category ASC, country ASC, __name__ ASC).

Se executar novamente a consulta no modo de análise, pode ver que o índice criado recentemente é selecionado para esta consulta e que a consulta é executada de forma muito mais rápida e eficiente.

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

O que se segue?