Comprendre les performances des requêtes à l'aide de Query Explain

Query Explain vous permet d'envoyer des requêtes Firestore au backend et de recevoir en retour des statistiques de performances détaillées sur l'exécution de ces requêtes. Elle fonctionne comme l'opération EXPLAIN [ANALYZE] dans de nombreux systèmes de bases de données relationnelles.

Les requêtes Query Explain peuvent être envoyées à l'aide des bibliothèques clientes du serveur Firestore.

Les résultats de la fonctionnalité Query Explain vous aident à comprendre comment vos requêtes sont exécutées, en vous montrant les inefficacités et l'emplacement des goulots d'étranglement côté serveur.

Explication de la requête:

  • Fournit des insights sur la phase de planification des requêtes afin que vous puissiez ajuster les index de vos requêtes et améliorer l'efficacité.
  • L'option d'analyse vous aide à comprendre vos coûts et vos performances par requête, et vous permet d'itérer rapidement différents modèles de requête afin d'optimiser leur utilisation.

Comprendre les options Query Explain : "default" et "analyze"

Les opérations Query Explain peuvent être effectuées à l'aide de l'option default (par défaut) ou de l'option analyze (analyse).

Avec l'option par défaut, Query Explain planifie la requête, mais ignore l'étape d'exécution. Cette opération renvoie des informations sur les étapes du planificateur. Cela vous permet de vérifier qu'une requête dispose des index nécessaires et d'identifier les index utilisés. Cela vous aidera à vérifier, par exemple, qu'une requête particulière utilise un index composite plutôt que de devoir se croiser sur de nombreux index différents.

Avec l'option d'analyse, l'option "Interroger Explain" permet de planifier et d'exécuter la requête. Cette commande renvoie toutes les informations de planification mentionnées précédemment, ainsi que les statistiques de l'environnement d'exécution de la requête. Ces informations incluent les informations de facturation de la requête, ainsi que des informations au niveau du système sur son exécution. Vous pouvez utiliser ces outils pour tester différentes configurations de requêtes et d'index afin d'optimiser leur coût et leur latence.

Combien coûte le service Query Explain ?

Lorsque vous utilisez Query Explain avec l'option par défaut, aucune opération d'index ou de lecture n'est effectuée. Quelle que soit la complexité de la requête, une opération de lecture est facturée.

Lorsque vous utilisez Query Explain avec l'option d'analyse, des opérations d'index et de lecture sont effectuées. La requête vous est donc facturée comme d'habitude. Aucuns frais supplémentaires ne sont facturés pour l'activité d'analyse, seulement les frais habituels pour la requête en cours d'exécution.

Utiliser Requête Expliquer avec l'option par défaut

Vous pouvez utiliser les bibliothèques clientes pour envoyer une requête d'option par défaut.

Notez que les requêtes sont authentifiées avec IAM, en utilisant les mêmes autorisations pour les opérations de requête standards. Les autres techniques d'authentification, telles que Firebase Authentication, sont ignorées. Pour en savoir plus, consultez le guide sur IAM pour les bibliothèques clientes de serveur.

Java (admin)

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

    
Nœud (administrateur)

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;

    

Le format exact de la réponse dépend de l'environnement d'exécution. Les résultats affichés peuvent être convertis au format JSON. Exemple :

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

Pour en savoir plus, consultez la documentation de référence sur le rapport Query Explain.

Utiliser Requête Expliquer avec l'option d'analyse

Vous pouvez utiliser les bibliothèques clientes pour envoyer une requête d'option d'analyse.

Notez que les requêtes sont authentifiées avec IAM, en utilisant les mêmes autorisations pour les opérations de requête standards. Les autres techniques d'authentification, telles que Firebase Authentication, sont ignorées. Pour en savoir plus, consultez le guide sur IAM pour les bibliothèques clientes de serveur.

Java (admin)

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

    
Nœud (administrateur)

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;

    

L'exemple suivant montre l'objet stats renvoyé en plus de planInfo. Le format exact de la réponse dépend de l'environnement d'exécution. L'exemple de réponse est au format 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",
               }
    }

}

Pour en savoir plus, consultez la documentation de référence sur le rapport Query Explain.

Interpréter les résultats et procéder à des ajustements

Examinons un exemple de scénario dans lequel nous interrogeons les films par genre et par pays de production.

À titre d'illustration, supposons l'équivalent de cette requête SQL.

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

Si nous utilisons l'option d'analyse, les métriques renvoyées montrent que la requête s'exécute sur deux index à champ unique, (category ASC, __name__ ASC) et (country ASC, __name__ ASC). Il analyse 16 500 entrées d'index, mais ne renvoie que 1 200 documents.

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

Pour optimiser les performances d'exécution de la requête, vous pouvez créer un index composite complet (category ASC, country ASC, __name__ ASC).

En exécutant à nouveau la requête avec l'option d'analyse, nous constatons que l'index nouvellement créé est sélectionné pour cette requête, et que celle-ci s'exécute beaucoup plus rapidement et plus efficacement.

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