Optimiser les performances des requêtes

Pour résoudre les problèmes liés aux requêtes lentes, utilisez Query Explain afin d'obtenir le plan d'exécution de la requête et le profil d'exécution au moment de l'exécution. La section suivante décrit les étapes à suivre pour optimiser les performances des requêtes en fonction du profil d'exécution :

Limiter le nombre de résultats

Utilisez le champ "records returned" (enregistrements renvoyés) dans l'arborescence d'exécution pour déterminer si la requête renvoie de nombreux documents. Envisagez de limiter le nombre de documents renvoyés à l'aide de l'étape limit(...). Cela réduit la taille des octets sérialisés des résultats lorsqu'ils sont renvoyés aux clients sur le réseau. Dans les cas où le nœud Limit est précédé d'un nœud MajorSort, le moteur de requête peut fusionner les nœuds Limit et MajorSort, et remplacer une matérialisation et un tri complets en mémoire par un tri TopN, ce qui réduit les besoins en mémoire de la requête.

Limiter la taille des documents de résultat

Envisagez de limiter la taille du document renvoyé à l'aide de select(...) pour ne renvoyer que les champs requis ou remove_fields(...) pour ignorer les champs trop volumineux. Cela permet de réduire les coûts de calcul et de mémoire liés au traitement des résultats intermédiaires, ainsi que la taille des octets sérialisés des résultats lorsqu'ils sont renvoyés aux clients sur le réseau. Dans les cas où tous les champs référencés dans la requête sont couverts par un index régulier, cela permet également à la requête d'être entièrement couverte par l'analyse d'index, ce qui évite d'avoir à récupérer des documents à partir du stockage principal.

Utiliser des index

Suivez les instructions ci-dessous pour configurer et optimiser les index.

Déterminer si la requête utilise un index

Vous pouvez déterminer si la requête utilise un index en vérifiant les nœuds feuilles dans l'arborescence d'exécution. Si le nœud feuille de l'arborescence d'exécution est un nœud TableScan, cela signifie que la requête n'utilise pas d'index et qu'elle analyse les documents à partir du stockage principal. Si un index est utilisé, le nœud feuille de l'arborescence d'exécution affiche l'ID et les champs d'index de l'index.

Identifier un meilleur index

Un index est utile pour une requête s'il peut réduire le nombre de documents que le moteur de requête doit récupérer à partir du stockage principal ou si son ordre de champ peut répondre aux exigences de tri de la requête.

Si un index est utilisé pour une requête, mais que le moteur de requête récupère et ignore toujours de nombreux documents, comme l'indique un nœud Scan qui renvoie de nombreux enregistrements suivi d'un nœud Filter qui en renvoie peu, cela signifie que le prédicat de requête satisfait à l'aide de l'index n'est pas sélectif. Pour créer un index plus adapté, consultez Créer des index.

Si un index est utilisé pour une requête, mais que le moteur de requête effectue toujours un réordonnancement en mémoire de l'ensemble de résultats, comme l'indique un nœud MajorSort dans l'arborescence d'exécution de la requête, cela signifie que l'index utilisé ne peut pas être utilisé pour répondre aux exigences de tri de la requête. Pour créer un index plus adapté, consultez la section suivante.

Créer des index

Suivez la documentation sur la gestion des index pour créer des index. Pour vous assurer que votre requête peut utiliser des index, créez des index réguliers (et non Multikey) avec des champs dans l'ordre suivant :

  1. Tous les champs qui seront utilisés dans les opérateurs d'égalité. Pour maximiser les chances de réutilisation entre les requêtes, ordonnez les champs par ordre décroissant d'occurrence des champs dans les opérateurs d'égalité entre les requêtes.
  2. Tous les champs sur lesquels le tri sera effectué (dans le même ordre).
  3. Les champs qui seront utilisés dans les opérateurs de plage ou d'inégalité par ordre décroissant de sélectivité des contraintes de requête.
  4. Les champs qui seront renvoyés dans le cadre d'une requête dans l'index : l'inclusion de ces champs dans l'index permet à l'index de couvrir la requête et d'éviter d'avoir à récupérer le document à partir du stockage principal.

Forcer une analyse d'index ou de table

Lorsque vous interrogez Cloud Firestore, il utilise automatiquement tous les index susceptibles d' améliorer l'efficacité de la requête. Par conséquent, vous n'avez pas besoin de spécifier d'index pour vos requêtes. Toutefois, pour les requêtes essentielles à votre charge de travail, nous vous recommandons d'utiliser l'option forceIndex pour des performances plus cohérentes.

Dans certains cas, Cloud Firestore peut choisir un index qui augmente la latence des requêtes. Si vous avez suivi la procédure de dépannage des régressions de performances et vérifié qu'il est judicieux d'essayer un autre index pour la requête, vous pouvez spécifier l'index à l'aide de l'option forceIndex.

Vous pouvez utiliser l'option forceIndex sur n'importe quelle étape d'entrée des opérations de pipeline pour remplacer le plan de requête par défaut de Cloud Firestore's et spécifier un index à utiliser, ou pour forcer une analyse de table.

Forcer un index spécifique

Pour forcer la requête à utiliser un index spécifique, fournissez l'ID de l'index sous forme de chaîne à l'option forceIndex. Vous trouverez l'ID de l'index dans la console ou dans les messages d'erreur.

L'exemple suivant force le planificateur à utiliser l'index avec l'ID CICAgOi36pgK :

Node.js
// Force Planner to use Index ID CICAgOi36pgK
await db.pipeline()
  .collectionGroup({ collectionId: "customers", forceIndex: "CICAgOi36pgK" })
  .limit(100)
  .execute();
Java
// Force Planner to use Index ID CICAgOi36pgK
Pipeline.Snapshot results1 =
    firestore.pipeline()
      .collectionGroup("customers", new CollectionGroupOptions()
          .withHints(new CollectionHints().withForceIndex("CICAgOi36pgK")))
      .limit(100)
      .execute().get();
Go
// Force Planner to use Index ID CICAgOi36pgK
snapshot1 := client.Pipeline().
	CollectionGroup("customers", firestore.WithForceIndex("CICAgOi36pgK")).
	Limit(100).
	Execute(ctx)

Voici quelques cas d'utilisation pour forcer un index spécifique :

  • Tester les performances de différents index.
  • S'assurer qu'un index spécifique et connu pour être optimal est utilisé pour une requête.
  • Remplacer l'optimiseur lorsque son choix par défaut n'est pas optimal pour une requête particulière.

Si l'index spécifié est introuvable, la requête échoue.

Forcer une analyse de table

Une analyse de table lit les documents de la collection ou du groupe de collections sans utiliser d'index secondaire. Pour forcer une analyse de table, définissez forceIndex sur primary.

L'exemple suivant force une analyse de table :

// Force Planner to only do a Full-Table Scan
db.pipeline()
  .collectionGroup({ collectionId: "customers", forceIndex: "primary" })
  .limit(100)

Vous pouvez utiliser une analyse de table dans les cas suivants :

  • Pour les collections très petites où la surcharge d'index n'est pas justifiée.
  • Pour les requêtes qui accèdent à la plupart des documents d'une collection.
  • Pour le débogage et les comparaisons de performances.

Utiliser forceIndex avec Query Explain

Vous pouvez utiliser Query Explain, en particulier avec l'option analyze, pour observer les effets de forceIndex :

  • Vérifiez que Cloud Firestore a utilisé l'index spécifié dans forceIndex en vérifiant l'ID de l'index dans les nœuds feuilles de l'arborescence d'exécution.
  • Vérifiez qu'un nœud TableScan apparaît dans le plan lorsque vous utilisez forceIndex: "primary".
  • Comparez les métriques de performances (telles que la latence, les documents analysés et les entrées d'index analysées) avec et sans forceIndex pour affiner les performances des requêtes.

Bonnes pratiques pour forceIndex

Bien que forceIndex offre plus de contrôle sur l'exécution des requêtes, Cloud Firestore's l'optimiseur de requêtes est généralement efficace pour la plupart des cas d'utilisation. Tenez compte des bonnes pratiques suivantes lorsque vous utilisez forceIndex :

  • Utilisez forceIndex avec discernement. Si vous constatez des performances sous-optimales avec le plan de requête par défaut, utilisez Query Explain pour diagnostiquer le problème avant de forcer un index.
  • Lorsque vous utilisez forceIndex, veillez à tester vos requêtes avec des volumes de données réalistes pour comprendre leurs performances et leurs caractéristiques de coût.
  • Évitez d'utiliser forceIndex: "primary" sur de grandes collections dans les environnements de production.