Usar Explicación de consultas

Explicación de consulta te permite enviar consultas del modo Datastore al backend y recibir estadísticas de rendimiento detalladas sobre la ejecución de consultas de backend. Funciona como la operación EXPLAIN ANALYZE en muchos sistemas de base de datos relacional.

Puedes enviar solicitudes de explicación de consultas con las bibliotecas cliente del modo Datastore.

Los resultados de la explicación de consultas te ayudan a comprender cómo se ejecutan tus consultas y te muestran las ineficiencias y la ubicación de los probables cuellos de botella en el servidor.

Explicación de la consulta:

  • Proporciona estadísticas sobre la fase de planificación para que puedas ajustar tus índices de consulta y aumentar la eficiencia.
  • Te ayuda a comprender el costo y el rendimiento por consulta y te permite iterar con rapidez por diferentes patrones de consulta para optimizar su uso.

Comprende las opciones de Explicación de consultas: Predeterminada y analizada

Las operaciones de explicación de consultas se pueden realizar con la opción predeterminada o la opción analizar.

Con la opción predeterminada, Explicación de consulta planifica la consulta, pero omite la etapa de ejecución. Esto devolverá la información de la etapa del planificador. Puedes usar esta opción para verificar que una consulta tenga los índices necesarios y comprender cuáles se usan. Esto te ayudará a verificar, por ejemplo, si una consulta en particular usa un índice compuesto en lugar de tener que cruzarse en muchos índices diferentes.

Con la opción de análisis, Query Explain planifica y ejecuta la consulta. Esto muestra toda la información de planificador mencionada anteriormente junto con las estadísticas del tiempo de ejecución de ejecución de la consulta. Esto incluirá los datos de facturación y las estadísticas a nivel del sistema sobre la ejecución de la consulta. Puedes usar estas herramientas para probar varias configuraciones de índices y consultas a fin de optimizar su costo y latencia.

¿Cuánto cuesta una explicación de consultas?

Cuando una consulta se explica con la opción predeterminada, no se realizan operaciones de índice ni de lectura. Sin importar la complejidad de la consulta, se cobra una operación de lectura.

Cuando se explica una consulta con la opción de analizar, se realizan las operaciones de indexación y lectura, por lo que se te cobra por la consulta como de costumbre. No se aplican cargos adicionales por la actividad de análisis, solo se cobra el cargo habitual por la consulta que se ejecuta.

Ejecuta una consulta con la opción predeterminada

Puedes usar una biblioteca cliente para enviar una solicitud de opción predeterminada.

Ten en cuenta que los resultados de explicación de consultas se autentican con Identity and Access Management, con los mismos permisos para las operaciones de consulta regulares.

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente del modo Datastore, consulta Bibliotecas cliente del modo Datastore. Si deseas obtener más información, consulta la documentación de referencia de la API Java del modo Datastore.

Para autenticarte en el modo Datastore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo 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)));
  }
}

Consulta el campo indexes_used en la respuesta para obtener información sobre los índices usados en el plan de consultas:

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

Para obtener más información sobre el informe, consulta la referencia del informe.

Ejecuta una consulta con la opción de análisis

Puedes usar una biblioteca cliente para enviar una solicitud de opción predeterminada.

Ten en cuenta que los resultados del análisis de consultas se autentican con Identity and Access Management (IAM), con los mismos permisos que las operaciones de consulta regulares.

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente del modo Datastore, consulta Bibliotecas cliente del modo Datastore. Si deseas obtener más información, consulta la documentación de referencia de la API Java del modo Datastore.

Para autenticarte en el modo Datastore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo 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);
    }
  }
}

Consulta el objeto executionStats para encontrar información de generación de perfiles de consulta, como la siguiente:

{
    "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 obtener más información sobre el informe, consulta la referencia del informe.

Interpreta los resultados y haz ajustes

En la siguiente situación de ejemplo, se consultan películas por género y país de producción, y se muestra cómo optimizar los índices que usa la consulta.

Para obtener más información sobre el informe, consulta la referencia del informe de explicación de consultas.

A modo de ejemplo, supongamos el equivalente de esta consulta en SQL.

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

Si usamos la opción de analizar, el siguiente resultado del informe muestra que la consulta se ejecuta en los índices de campo único (category ASC, __name__ ASC) y (country ASC, __name__ ASC). Analiza 16,500 entradas de índice, pero solo muestra 1,200 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 optimizar el rendimiento de la ejecución de la consulta, puedes crear un índice compuesto completamente cubierto (categoría ASC, país ASC, __name__ ASC).

Si vuelves a ejecutar la consulta en el modo de análisis, podemos ver que el índice recién creado está seleccionado para esta consulta y que esta se ejecuta mucho más rápido y de manera más 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",
               }
    }
}

¿Qué sigue?