Optimisation des index

Cette page décrit les concepts à prendre en compte lors de la sélection des index Cloud Firestore en mode Datastore pour votre application.

Cloud Firestore en mode Datastore offre des performances de requête élevées grâce à l'utilisation d'index pour toutes les requêtes. Les performances de la plupart des requêtes dépendent de la taille de l'ensemble de résultats et non de la taille totale de la base de données.

Cloud Firestore en mode Datastore définit des index intégrés pour chaque propriété d'une entité. Ces index de propriétés uniques sont adaptés à des types de requêtes simples. Cloud Firestore en mode Datastore présente une fonctionnalité de fusion d'index qui permet à votre base de données de fusionner des index intégrés afin d'être disponible pour d'autres requêtes. Pour les requêtes plus complexes, vous devez définir des index composites à l'avance.

Cette page s'intéresse à la fonctionnalité de fusion d'index, car elle concerne deux aspects importants de l'optimisation des index :

  • Accélérer les requêtes
  • Réduire le nombre d'index composites

L'exemple suivant montre comment opère la fonctionnalité de fusion d'index.

Filtrage des entités Photo

Prenons l'exemple d'une base de données en mode Datastore avec des entités de genre Photo :

Photo
Valeur Type de valeur Description
owner_id Chaîne ID utilisateur
tag Tableau de chaînes Mots clés tokenisés
size Entier Énumération :
  • 1 icon
  • 2 medium
  • 3 large
coloration Entier Énumération :
  • 1 black & white
  • 2 color

Imaginons que vous ayez besoin d'une fonctionnalité permettant aux utilisateurs d'interroger les entités Photo en fonction d'un AND logique des éléments suivants :

  • Jusqu'à trois filtres basés sur les propriétés :

    • owner_id
    • size
    • coloration
  • Une chaîne de recherche tag. L'application segmente la chaîne de recherche en tags et ajoute un filtre pour chaque tag.

    Par exemple, l'application transforme la chaîne de recherche outside, family en filtres de requête tag=outside et tag=family.

Grâce aux index intégrés et à la fonctionnalité de fusion d'index de Cloud Firestore en mode Datastore, vous pouvez répondre aux exigences d'index de cette fonction de filtre Photo sans ajouter d'index composites.

Les index intégrés pour les entités Photo acceptent les requêtes à filtre unique, telles que :

Python

query_owner_id = client.query(
        kind='Photo',
        filters=[('owner_id', '=', 'user1234')])

    query_size = client.query(
        kind='Photo',
        filters=[('size', '=', 2)])

    query_coloration = client.query(
        kind='Photo',
        filters=[('coloration', '=', 2)])

La fonction de filtre Photo nécessite également des requêtes qui combinent plusieurs filtres d'égalité avec un AND logique :

Python

query_all_properties = client.query(
        kind='Photo',
        filters=[('owner_id', '=', 'user1234'),
                 ('size', '=', 2),
                 ('coloration', '=', 2),
                 ('tag', '=', 'family')])

Cloud Firestore en mode Datastore est en mesure d'accepter ces requêtes en fusionnant des index intégrés.

Fusion d'index

Cloud Firestore en mode Datastore peut utiliser la fusion d'index lorsque votre requête et vos index répondent à l'ensemble des contraintes suivantes :

  • La requête n'utilise que des filtres d'égalité (=).
  • Il n'existe aucun index composite qui corresponde parfaitement aux filtres et à l'ordre de tri de la requête.
  • Chaque filtre d'égalité correspond à au moins un index existant ayant le même ordre de tri que la requête.

Dans ce cas, Cloud Firestore en mode Datastore peut utiliser des index existants pour accepter la requête, ce qui évite d'avoir à configurer un index composite supplémentaire.

Lorsque plusieurs index sont triés selon les mêmes critères, Cloud Firestore en mode Datastore peut fusionner les résultats de plusieurs analyses d'index pour trouver les résultats communs à tous ces index. Cloud Firestore en mode Datastore peut fusionner des index intégrés, car tous effectuent le tri des valeurs par clé d'entité.

En fusionnant les index intégrés, Cloud Firestore en mode Datastore accepte les requêtes avec des filtres d'égalité appliqués à plusieurs propriétés :

Python

query_all_properties = client.query(
        kind='Photo',
        filters=[('owner_id', '=', 'user1234'),
                 ('size', '=', 2),
                 ('coloration', '=', 2),
                 ('tag', '=', 'family')])

Cloud Firestore en mode Datastore peut également fusionner des résultats d'index à partir de plusieurs sections d'un même index. En fusionnant différentes sections de l'index intégré pour la propriété tag, Cloud Firestore en mode Datastore accepte les requêtes combinant plusieurs filtres tag dans un AND logique :

Python

query_tag = client.query(
        kind='Photo',
        filters=[('tag', '=', 'family'),
                 ('tag', '=', 'outside'),
                 ('tag', '=', 'camping')])

    query_owner_size_color_tags = client.query(
        kind='Photo',
        filters=[('owner_id', '=', 'user1234'),
                 ('size', '=', 2),
                 ('coloration', '=', 2),
                 ('tag', '=', 'family'),
                 ('tag', '=', 'outside'),
                 ('tag', '=', 'camping')])

Les requêtes acceptées par les index intégrés fusionnés complètent l'ensemble des requêtes nécessaires à la fonction de filtrage Photo. Notez que la compatibilité avec la fonction de filtrage Photo n'a pas nécessité d'index composites supplémentaires.

Lorsque vous sélectionnez les index optimaux pour votre application, il est important de comprendre la fonctionnalité de fusion d'index. La fusion d'index apporte à Cloud Firestore en mode Datastore une plus grande flexibilité d'interrogation, mais un compromis en termes de performances est à rechercher. La section suivante décrit les performances de la fusion d'index et explique comment l'ajout d'index composites peut les améliorer.

Optimiser la sélection de vos index

Cette section décrit les caractéristiques de la fusion d'index et les deux possibilités d'optimisation pouvant être envisagées :

  • Ajouter des index composites pour accélérer les requêtes qui s'appuient sur des index fusionnés
  • Réduire le nombre d'index composites en exploitant des index fusionnés

Performances de fusion d'index

Dans une fusion d'index, Cloud─Firestore en mode Datastore fusionne efficacement les index à l'aide d'un algorithme de jointure par fusion en zig-zag. À l'aide de cet algorithme, le mode Datastore joint les correspondances potentielles de plusieurs analyses d'index pour produire un ensemble de résultats qui correspond à une requête. La fusion d'index combine les composants de filtre au moment de la lecture plutôt qu'au moment de l'écriture. Contrairement à la plupart des requêtes de Cloud Firestore en mode Datastore où les performances ne dépendent que de la taille de l'ensemble de résultats, les performances des requêtes de fusion d'index dépendent des filtres de la requête et du nombre de correspondances potentielles dont la base de données doit tenir compte.

Les performances optimales d'une fusion d'index sont obtenues lorsque chaque correspondance potentielle dans un index satisfait les filtres de requête. Dans ce cas, les performances se traduisent par O(R * I), où R correspond à la taille de l'ensemble de résultats et I au nombre d'index analysés.

Le pire des cas se produit lorsque la base de données doit tenir compte de nombreuses correspondances potentielles, mais que peu d'entre elles répondent aux filtres de requête. Dans ce cas, les performances se traduisent par O(S), où S est la taille du plus petit ensemble d'entités potentielles d'une seule analyse d'index.

Les performances réelles dépendent de la forme des données. Le nombre moyen d'entités prises en compte pour chaque résultat renvoyé est de O(S/(R * I)). Les requêtes sont moins performantes lorsque plusieurs entités correspondent à chaque recherche d'index, mais que peu d'entités correspondent à la requête dans son ensemble, ce qui signifie que R est petit et S important.

Quatre éléments permettent d'atténuer ce risque :

  • Le planificateur de requêtes ne recherche pas d'entité tant qu'il ignore si l'entité correspond à la requête entière.

  • L'algorithme en zig-zag n'a pas besoin de trouver tous les résultats pour renvoyer le résultat suivant. Si vous demandez les 10 premiers résultats, vous ne payez que la latence nécessaire à leur obtention.

  • L'algorithme en zigzag ignore une grande partie des faux positifs. Le pire des cas ne se produit que si les résultats de faux positifs sont parfaitement imbriqués (par ordre de tri) entre les analyses.

  • La latence dépend du nombre d'entités trouvées dans chaque analyse d'index, et non du nombre d'entités correspondant à chaque filtre. Comme indiqué dans la section suivante, vous pouvez ajouter des index composites pour améliorer les performances de la fusion d'index.

Accélérer une requête de fusion d'index

Lorsque Cloud Firestore en mode Datastore effectue la fusion d'index, chaque analyse d'index est souvent mappée à un seul filtre de la requête. Vous pouvez améliorer les performances des requêtes en ajoutant des index composites correspondant à plusieurs filtres de la requête.

Examinez cette requête :

Python

query_owner_size_tag = client.query(
        kind='Photo',
        filters=[('owner_id', '=', 'username'),
                 ('size', '=', 2),
                 ('tag', '=', 'family')])

Chaque filtre correspond à une analyse d'index dans les index intégrés suivants :

    Index(Photo, owner_id)
    Index(Photo, size)
    Index(Photo, tag)
    

Si vous ajoutez l'index composite Index(Photo, owner_id, size), la requête correspond à deux analyses d'index au lieu de trois :

    #  Satisfies both 'owner_id=username' and 'size=1'
    Index(Photo, owner_id, size)
    Index(Photo, tag)
    

Prenons l'exemple d'un scénario comportant un grand nombre de grandes images et d'images en noir et blanc, mais peu d'images panoramiques de grande taille. En utilisant la fusion d'index intégrés, le filtrage des requêtes pour les images panoramiques et les images en noir et blanc sera lent :

Python

query_size_coloration = client.query(
        kind='Photo',
        filters=[('size', '=', 2),
                 ('coloration', '=', 1)])

Pour améliorer les performances des requêtes, vous pouvez réduire la valeur de S (le plus petit ensemble d'entités dans une seule analyse d'index) dans O(S/(R * I)) en ajoutant l'index composite suivant :

    Index(Photo, size, coloration)
    

Comparé à l'utilisation de deux index intégrés, cet index composite produit moins de résultats potentiels pour les deux mêmes filtres de requête. Cette approche améliore considérablement les performances au prix d'un indice supplémentaire.

Réduire le nombre d'index composites à l'aide de la fusion d'index

Bien que les index composites qui correspondent exactement aux filtres d'une requête donnent les meilleurs résultats, il n'est pas toujours possible d'en ajouter un pour chaque combinaison de filtres. Vous devez équilibrer vos index composites par rapport aux éléments suivants :

  • Limites d'un index composite :

    Limit Volume
    Nombre maximal d'index composites pour un projet 200
    Valeur globale maximale pour les tailles des entrées d'index composite d'une entité 2 Mio
    Valeur globale maximale par entité pour les éléments suivants :
    • nombre de valeurs de propriété indexée
    • nombre d'entrées d'index composite
    20 000
  • Coûts de stockage de chaque index supplémentaire.

Des problèmes d'indexation se produisent souvent avec les champs à valeurs multiples, tels que la propriété tag des entités Photo.

Imaginons que la fonction de filtrage Photo doive désormais accepter les clauses par ordre décroissant basées sur quatre propriétés supplémentaires :

Photo
Valeur Type de valeur Description
date_added Entier Date/Heure
rating Float Note globale des utilisateurs
comment_count Entier Le nombre de commentaires
download_count Entier Nombre de téléchargements

Si vous ne tenez pas compte du champ tag, il est possible de sélectionner des index composites qui correspondent à chaque combinaison de filtres Photo :

    Index(Photo, owner_id, -date_added)
    Index(Photo, owner_id, -comments)
    Index(Photo, size, -date_added)
    Index(Photo, size, -comments)
    ...
    Index(Photo, owner_id, size, -date_added)
    Index(Photo, owner_id, size, -comments)
    ...
    Index(Photo, owner_id, size, coloration, -date_added)
    Index(Photo, owner_id, size, coloration, -comments)
    

Le nombre total d'index composites est le suivant :

    2^(number of filters) * (number of different orders) = 2 ^ 3 * 4 = 32 composite indexes
    

Si vous essayez d'appliquer jusqu'à trois filtres tag, le nombre total d'index composites dépasse la limite de 200 :

    2 ^ (3 + 3 tag filters) * 4 = 256 indexes.
    

Les index qui incluent des propriétés à plusieurs valeurs telles que tag entraînent également des problèmes d'index exponentiels qui augmentent les coûts de stockage.

Pour appliquer les filtres au champ tag de cette fonctionnalité, vous pouvez réduire le nombre total d'index en vous appuyant sur des index fusionnés. L'ensemble d'index composites suivant est le minimum requis pour accepter la fonction de filtre Photo avec classement :

    Index(Photo, owner_id, -date_added)
    Index(Photo, owner_id, -rating)
    Index(Photo, owner_id, -comments)
    Index(Photo, owner_id, -downloads)
    Index(Photo, size, -date_added)
    Index(Photo, size, -rating)
    Index(Photo, size, -comments)
    Index(Photo, size, -downloads)
    ...
    Index(Photo, tag, -date_added)
    Index(Photo, tag, -rating)
    Index(Photo, tag, -comments)
    Index(Photo, tag, -downloads)
    

Le nombre d'index composites définis est le suivant :

    (number of filters + 1) * (number of orders) = 7 * 4 = 28
    

La fusion d'index offre également ces avantages  :

  • Permet à une entité Photo d'accepter jusqu'à 1 000 tags, sans limite du nombre de filtres tag par requête.
  • Réduit le nombre total d'index, ce qui réduit les coûts de stockage.

Sélectionner des index pour votre application

Il existe deux approches pour sélectionner des index optimaux pour votre base de données en mode Datastore :

  • Utiliser la fusion d'index pour accepter d'autres requêtes

    • Nécessite moins d'index composites
    • Réduit le coût du stockage par entité
    • Évite les index exponentiels
    • Les performances dépendent de la forme des données
  • Définir un index composite qui correspond à plusieurs filtres dans une requête

    • Améliore les performances des requêtes
    • Performances des requêtes cohérentes et non dépendantes de la forme des données
    • La limite est de 200 index composites
    • Augmente le coût du stockage par entité

Lors de la détermination des index optimaux pour votre application, la réponse peut changer à mesure que la forme des données évolue. L'analyse des performances à partir d'un échantillon de données donne une idée relativement précise des requêtes courantes de votre application et de celles qui sont ralenties. Ces informations permettent de déterminer s'il convient d'ajouter des index afin d'améliorer les performances des requêtes à la fois courantes et lentes.