Conocer el rendimiento de las consultas con Query Explain

Query Explain te permite enviar consultas de Firestore en modo nativo al backend y recibir estadísticas detalladas sobre el rendimiento de la ejecución de consultas del backend. Funciona como la operación EXPLAIN [ANALYZE] en muchos sistemas de bases de datos relacionales.

Las solicitudes de explicación de consultas se pueden enviar mediante las bibliotecas de cliente del servidor de Firestore.

Los resultados de Explicación de consulta te ayudan a entender cómo se ejecutan tus consultas, ya que te muestran las ineficiencias y la ubicación de los posibles cuellos de botella del lado del servidor.

Explicación de la consulta:

  • Proporciona información valiosa sobre la fase de planificación de consultas para que puedas ajustar tus índices de consultas y aumentar la eficiencia.
  • La opción de análisis te ayuda a conocer el coste y el rendimiento de cada consulta y te permite iterar rápidamente entre diferentes patrones de consulta para optimizar su uso.

Información sobre las opciones de explicación de consultas: predeterminada y analizar

Las operaciones de Query Explain se pueden realizar con la opción predeterminada o con la opción analizar.

Con la opción predeterminada, Query Explain planifica la consulta, pero se salta la fase de ejecución. Se devolverá información sobre la fase de planificación. Puedes usar esta función para comprobar si una consulta tiene los índices necesarios y saber qué índices se utilizan. De esta forma, podrás verificar, por ejemplo, que una consulta concreta está usando un índice compuesto en lugar de tener que intersectar muchos índices diferentes.

Con la opción de analizar, Query Explain planifica y ejecuta la consulta. De esta forma, se devuelve toda la información del planificador mencionada anteriormente, junto con las estadísticas del tiempo de ejecución de la consulta. Esto incluirá la información de facturación de la consulta junto con estadísticas a nivel de sistema sobre la ejecución de la consulta. Puede usar esta herramienta para probar varias configuraciones de consultas e índices con el fin de optimizar su coste y latencia.

¿Qué coste tiene Query Explain?

Cuando usas Query Explain con la opción predeterminada, no se realizan operaciones de lectura ni de índice. Independientemente de la complejidad de la consulta, se cobra una operación de lectura.

Cuando usas Query Explain con la opción de analizar, se realizan operaciones de lectura e índice, por lo que se te cobra por la consulta como de costumbre. No se aplica ningún cargo adicional por la actividad de análisis, solo el cargo habitual por la consulta que se ejecuta.

Usar Query Explain con la opción predeterminada

Puede usar las bibliotecas de cliente para enviar una solicitud de opción predeterminada.

Ten en cuenta que las solicitudes se autentican con IAM, usando los mismos permisos que para las operaciones de consulta habituales. Se ignoran otras técnicas de autenticación, como la autenticación de Firebase. Para obtener más información, consulta la guía sobre IAM para bibliotecas de cliente de servidor.

Java (administración)

Query q = db.collection("col").whereGreaterThan("a", 1);
ExplainOptions options = ExplainOptions.builder().build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();
ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();

    
Nodo (administrador)

const q = db.collection('col').where('country', '=', 'USA');
const options = { analyze : 'false' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;

    

El formato exacto de la respuesta depende del entorno de ejecución. Los resultados devueltos se pueden convertir a JSON. Por ejemplo:

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

Para obtener más información, consulta la referencia del informe "Explicación de la consulta".

Usar Query Explain con la opción de análisis

Puedes usar las bibliotecas de cliente para enviar una solicitud de opción de análisis.

Ten en cuenta que las solicitudes se autentican con IAM, usando los mismos permisos que para las operaciones de consulta habituales. Se ignoran otras técnicas de autenticación, como la autenticación de Firebase. Para obtener más información, consulta la guía sobre IAM para bibliotecas de cliente de servidor.

Java (administración)

Query q = db.collection("col").whereGreaterThan("a", 1);

ExplainOptions options = ExplainOptions.builder().setAnalyze(true).build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();

ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
ExecutionStats stats = metrics.getExecutionStats();

    
Nodo (administrador)

const q = db.collection('col').where('country', '=', 'USA');

const options = { analyze : 'true' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;
const indexesUsed = plan.indexesUsed;
const stats = metrics.executionStats;

    

En el siguiente ejemplo se muestra el objeto stats devuelto, además de planInfo. El formato exacto de la respuesta depende del entorno de ejecución. La respuesta de ejemplo está en formato JSON.

{
    "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, consulta la referencia del informe "Explicación de la consulta".

Interpretar los resultados y hacer ajustes

Veamos un ejemplo en el que consultamos películas por género y país de producción.

Para ilustrarlo, supongamos que se trata del equivalente de esta consulta de SQL.

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

Si usamos la opción de análisis, las métricas devueltas muestran que la consulta se ejecuta en dos índices de un solo campo, (category ASC, __name__ ASC) y (country ASC, __name__ ASC). Analiza 16.500 entradas de índice, pero devuelve solo 1200 documentos.

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},
        {"query_scope": "Collection", "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 totalmente cubierto (category ASC, country ASC, __name__ ASC).

Si volvemos a ejecutar la consulta con la opción de análisis, veremos que se ha seleccionado el índice recién creado para esta consulta y que la consulta se ejecuta mucho más rápido y de forma más eficiente.

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "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",
               }
    }
}