Bonnes pratiques

Référez-vous à ces bonnes pratiques lorsque vous créez une application utilisant Firestore.

Zone de la base de données

Lorsque vous créez votre instance de base de données, sélectionnez la zone de base de données la plus proche de vos utilisateurs et des ressources de calcul. Les sauts de réseau importants sont plus sujets aux erreurs et augmentent la latence des requêtes.

Pour optimiser la disponibilité et la durabilité de votre application, sélectionnez une zone multirégionale et placez les ressources de calcul critiques dans au moins deux régions.

Sélectionnez une zone régionale si vous souhaitez réduire vos coûts, réduire la latence d'écriture si votre application est sensible à la latence, ou co-localiser votre application avec d'autres ressources GCP.

ID de document

  • Évitez les ID de document . et ...
  • Évitez d'utiliser des barres obliques / dans les ID de document.
  • N'utilisez pas des ID de document monotones croissants, tels que :

    • Customer1, Customer2, Customer3, ...
    • Product 1, Product 2, Product 3, ...

    Ces ID séquentiels peuvent générer des hotspots qui ont un impact sur la latence.

Noms des champs

  • Évitez les caractères suivants dans les noms de champs, car ils nécessitent un échappement supplémentaire :

    • . point
    • [ crochet ouvrant
    • ] crochet fermant
    • * astérisque
    • ` accent grave

Index

Réduire la latence d'écriture

Le principal facteur contribuant à la latence d'écriture est le fanage d'index. Voici les bonnes pratiques à suivre pour réduire le fanage d'index:

  • Définissez des exceptions d'index au niveau de la collection. Il est facile de désactiver l'indexation décroissante et par tableau par défaut. La suppression des valeurs indexées inutilisées permet également de réduire les coûts de stockage.

  • Réduire le nombre de documents dans une transaction. Pour écrire un grand nombre de documents, envisagez d'utiliser un éditeur par lot plutôt que l'éditeur par lot atomique.

Exclusions d'index

Pour la plupart des applications, vous pouvez utiliser l'indexation automatique et les liens des messages d'erreur pour gérer vos index. Toutefois, vous pouvez ajouter des exclusions de champs uniques dans les cas suivants :

Cas Description
Champs de chaîne volumineux

S'il existe un champ de chaîne contenant souvent des chaînes longues que vous n'utilisez pas pour les requêtes, vous pouvez réduire les coûts de stockage en excluant le champ de l'indexation.

Taux d'écriture élevés dans une collection contenant des documents avec des valeurs séquentielles

Si vous indexez un champ qui augmente ou diminue séquentiellement entre les documents d'une collection, comme un horodatage, le taux d'écriture maximal pour la collection est de 500 écritures par seconde. Si vous ne faites pas de requête en fonction du champ avec des valeurs séquentielles, vous pouvez exclure le champ de l'indexation pour contourner cette limite.

Par exemple, dans le cas d'un IoT avec un taux d'écriture élevé, une collection contenant des documents avec un champ d'horodatage peut approcher la limite des 500 écritures par seconde.

Champs TTL

Si vous utilisez des règles TTL (Time To Live), notez que le champ TTL doit être un code temporel. L'indexation sur les champs TTL est activée par défaut et peut affecter les performances à des taux de trafic plus élevés. Nous vous recommandons d'ajouter des exceptions de champ unique pour vos champs TTL.

Champs de tableau ou de carte volumineux

Les champs de tableau ou de mappage volumineux peuvent approcher la limite de 40 000 entrées d'index par document. Si vous ne faites pas de requête en fonction d'un champ de tableau ou de mappage volumineux, vous devez exclure ce champ de l'indexation.

Opérations de lecture et d'écriture

  • La fréquence maximale exacte à laquelle une application peut mettre à jour un seul document dépend fortement de la charge de travail. Pour plus d'informations, consultez la section Mises à jour d'un seul document.

  • Utilisez des appels asynchrones, le cas échéant, au lieu d'appels synchrones. Les appels asynchrones minimisent l'impact de la latence. Par exemple, prenons une application qui a besoin du résultat d'une recherche de document et des résultats d'une requête pour pouvoir afficher une réponse. Si la recherche et la requête ne dépendent pas des données, il n'est pas nécessaire d'attendre de manière synchrone que la recherche se termine avant de lancer la requête.

  • N'utilisez pas de décalages. Utilisez plutôt des curseurs. L'utilisation d'un décalage permet uniquement d'éviter de renvoyer les documents ignorés à l'application, mais ces derniers sont tout de même récupérés en interne. Les documents ignorés ont une incidence sur la latence de la requête, et l'application est facturée pour les opérations de lecture nécessaires à leur récupération.

Nouvelles tentatives de transactions

Les SDK et bibliothèques clientes de Firestore retentent automatiquement les transactions ayant échoué pour gérer les erreurs temporaires. Si votre application accède à Firestore via les API REST ou RPC directement au lieu d'utiliser un SDK, elle doit mettre en œuvre les nouvelles tentatives de transactions pour améliorer la fiabilité.

Mises à jour en temps réel

Pour connaître les bonnes pratiques concernant les mises à jour en temps réel, consultez la section Comprendre les requêtes en temps réel à grande échelle.

Concevoir des solutions évolutives

Les bonnes pratiques suivantes décrivent comment éviter les situations qui créent des conflits.

Mises à jour d'un seul document

Lorsque vous concevez votre application, réfléchissez à la rapidité avec laquelle elle met à jour des documents individuels. Le meilleur moyen de caractériser les performances de votre charge de travail est d'effectuer des tests de charge. Le débit maximal exact qu'une application peut mettre à jour pour un seul document dépend fortement de la charge de travail. Ces facteurs incluent le taux d'écriture, la contention entre les requêtes et le nombre d'index concernés.

Une opération d'écriture de document met à jour le document et les éventuels index associés, et Firestore applique de manière synchrone l'opération d'écriture sur un quorum de réplicas. À des taux d'écriture suffisamment élevés, la base de données commencera à rencontrer des conflits, une latence plus élevée ou d'autres erreurs.

Taux de lecture, d'écriture et de suppression élevés pour une plage de documents restreinte

Évitez les taux de lecture ou d'écriture élevés sur les documents qui sont proches d'un point de vue lexicographique, ou votre application rencontrera des erreurs liées à un conflit. Ce problème, appelé "hotspotting", peut se produire si votre application effectue l'une des opérations suivantes :

  • Elle crée des documents à un taux très élevé et attribue ses propres ID monotones croissants.

    Firestore attribue les ID de document à l'aide d'un algorithme de diffusion. Vous ne devriez pas rencontrer de problème de hotspotting sur les opérations d'écriture si vous créez vos documents à l'aide d'ID de document automatiques.

  • Elle crée des documents à un taux élevé dans une collection contenant peu de documents.

  • Elle crée des documents avec un champ monotone croissant, comme un horodatage, à un taux très élevé.

  • Elle supprime les documents d'une collection à un taux élevé.

  • Elle effectue des opérations d'écriture dans la base de données à un taux très élevé sans augmenter progressivement le trafic.

Éviter de passer outre les données supprimées

Évitez les requêtes qui ignorent les données supprimées récemment. Une requête peut être amenée à ignorer un grand nombre d'entrées d'index si les premiers résultats de la requête ont été supprimés récemment.

Un exemple de charge de travail qui peut être amenée à ignorer de nombreuses données supprimées est celle qui tente de trouver les éléments de travail les plus anciens en file d'attente. La requête peut se présenter comme suit:

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Chaque fois que cette requête s'exécute, elle analyse les entrées d'index du champ created pour tous les documents récemment supprimés. Cela ralentit les requêtes.

Pour améliorer les performances, utilisez la méthode start_at pour trouver le meilleur point de départ. Exemple :

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

REMARQUE: L'exemple ci-dessus utilise un champ augmentant de façon linéaire, ce qui est un anti-modèle pour les taux d'écriture élevés.

Augmenter le trafic

Vous devez augmenter progressivement le trafic vers les nouvelles collections ou les documents qui sont proches d'un point de vue lexicographique afin de donner à Firestore le temps nécessaire pour préparer les documents à l'augmentation du trafic. Nous recommandons de commencer par un maximum de 500 opérations par seconde dans une nouvelle collection, puis d'augmenter le trafic de 50 % toutes les 5 minutes. Vous pouvez faire de même pour votre trafic en écriture, mais gardez à l'esprit les limites standards de Firestore. Assurez-vous que les opérations sont réparties de manière relativement uniforme sur la plage de clés. Nous appelons cette méthode "règle 500/50/5".

Migrer le trafic vers une nouvelle collection

La montée en puissance progressive est particulièrement importante si vous migrez le trafic de votre application d'une collection vers une autre. Une façon simple de gérer cette migration consiste à lire l'ancienne collection et, si le document n'existe pas, à lire la nouvelle collection. Toutefois, cela peut entraîner une augmentation soudaine du trafic vers les documents qui sont proches d'un point de vue lexicographique dans la nouvelle collection. Il est possible que Firestore ne puisse pas préparer efficacement la nouvelle collection à l'augmentation du trafic, en particulier si elle contient peu de documents.

Un problème similaire peut se produire si vous modifiez les ID de nombreux documents dans la même collection.

La stratégie à adopter pour migrer le trafic vers une nouvelle collection dépend de votre modèle de données. Vous trouverez ci-dessous un exemple de stratégie connue sous le nom de lectures parallèles. Vous devrez déterminer si cette stratégie est efficace pour vos données. L'impact financier des opérations parallèles pendant la migration sera un aspect important à prendre en compte.

Lectures parallèles

Pour mettre en œuvre des lectures parallèles lors de la migration du trafic vers une nouvelle collection, commencez par lire l'ancienne collection. Si le document est manquant, lisez la nouvelle collection. Un taux élevé de lectures de documents inexistants peut entraîner un problème de hotspotting. Vous devez faire en sorte d'augmenter progressivement la charge vers la nouvelle collection. Une meilleure stratégie consiste à copier l'ancien document dans la nouvelle collection, puis à supprimer l'ancien document. Augmentez progressivement les lectures parallèles afin que Firestore puisse gérer le trafic vers la nouvelle collection.

Une stratégie possible pour l'augmentation progressive des opérations de lecture ou d'écriture vers une nouvelle collection consiste à utiliser un hachage déterministe de l'ID utilisateur afin de sélectionner un pourcentage aléatoire d'utilisateurs qui tentent d'écrire de nouveaux documents. Assurez-vous que le résultat du hachage de l'ID utilisateur n'est pas faussé par votre fonction ou par le comportement des utilisateurs.

Pendant ce temps, exécutez une tâche par lot qui copie toutes les données des anciens documents dans la nouvelle collection. Votre tâche par lot doit éviter les opérations d'écriture sur des ID de document séquentiels afin d'empêcher les hotspots. Lorsque la tâche par lot est terminée, vous ne pouvez effectuer des opérations de lecture qu'à partir de la nouvelle collection.

Vous pouvez peaufiner cette stratégie en faisant migrer simultanément des petits lots d'utilisateurs. Ajoutez un champ au document de l'utilisateur, qui suit l'état de la migration de cet utilisateur. Sélectionnez un lot d'utilisateurs à migrer en fonction du hachage de l'ID utilisateur. Utilisez une tâche par lot pour migrer les documents pour ce lot d'utilisateurs, et utilisez des lectures parallèles pour les utilisateurs en cours de migration.

Veuillez noter qu'il n'est pas facile de revenir à la configuration précédente sans effectuer d'opérations d'écriture en double des anciennes et des nouvelles entités pendant la phase de migration. Ceci aurait pour effet d'augmenter les coûts de Firestore.

Confidentialité

  • Évitez de stocker des informations sensibles dans un ID de projet Cloud, dans la mesure où il peut être conservé au-delà de la durée de vie de votre projet.
  • Conformément aux bonnes pratiques de conformité des données, nous vous recommandons de ne pas stocker d'informations sensibles dans les noms de documents et de champs de documents.