Comprendre les lectures et les écritures à grande échelle

Lisez ce document pour prendre des décisions éclairées sur l'architecture de vos applications pour des performances et une fiabilité élevées. Ce document inclut des rubriques avancées sur Cloud Firestore. Si vous débutez avec Cloud Firestore, consultez plutôt le guide de démarrage rapide .

Cloud Firestore est une base de données flexible et évolutive pour le développement d'appareils mobiles, de sites Web et de serveurs à partir de Firebase et Google Cloud. Il est très simple de démarrer avec Cloud Firestore et d'écrire des applications riches et puissantes.

Pour vous assurer que vos applications continuent de fonctionner correctement à mesure que la taille de votre base de données et le trafic augmentent, il est utile de comprendre les mécanismes de lecture et d'écriture dans le backend Cloud Firestore. Vous devez également comprendre l'interaction de vos lectures et écritures avec la couche de stockage et les contraintes sous-jacentes qui peuvent affecter les performances.

Consultez les sections suivantes pour connaître les meilleures pratiques avant de concevoir votre application.

Comprendre les composants de haut niveau

Le diagramme suivant montre les composants de haut niveau impliqués dans une requête API Cloud Firestore.

Composants de haut niveau

SDK Cloud Firestore et bibliothèques clientes

Cloud Firestore prend en charge les SDK et les bibliothèques clientes pour différentes plates-formes. Même si une application peut effectuer des appels HTTP et RPC directs vers l'API Cloud Firestore, les bibliothèques clientes fournissent une couche d'abstraction pour simplifier l'utilisation de l'API et mettre en œuvre les meilleures pratiques. Ils peuvent également fournir des fonctionnalités supplémentaires telles qu'un accès hors ligne, des caches, etc.

Google Front End (GFE)

Il s'agit d'un service d'infrastructure commun à tous les services cloud de Google. Le GFE accepte les demandes entrantes et les transmet au service Google concerné (service Firestore dans ce contexte). Il fournit également d'autres fonctionnalités importantes, notamment la protection contre les attaques par déni de service.

Service Cloud Firestore

Le service Cloud Firestore effectue des contrôles sur la demande d'API, qui incluent l'authentification, l'autorisation, les contrôles de quota et les règles de sécurité, et gère également les transactions. Ce service Cloud Firestore comprend un client de stockage qui interagit avec la couche de stockage pour les lectures et écritures de données.

Couche de stockage Cloud Firestore

La couche de stockage Cloud Firestore est responsable du stockage des données et des métadonnées, ainsi que des fonctionnalités de base de données associées fournies par Cloud Firestore. Les sections suivantes décrivent comment les données sont organisées dans la couche de stockage Cloud Firestore et comment le système évolue. Apprendre comment les données sont organisées peut vous aider à concevoir un modèle de données évolutif et à mieux comprendre les bonnes pratiques dans Cloud Firestore.

Plages de touches et divisions

Cloud Firestore est une base de données NoSQL orientée document. Vous stockez les données dans des documents organisés en hiérarchies de collections . La hiérarchie de collection et l'ID du document sont traduits en une clé unique pour chaque document. Les documents sont logiquement stockés et classés lexicographiquement par cette clé unique. Nous utilisons le terme plage de touches pour désigner une plage de touches lexicographiquement contiguë.

Une base de données Cloud Firestore typique est trop volumineuse pour tenir sur une seule machine physique. Il existe également des scénarios dans lesquels la charge de travail sur les données est trop lourde à gérer pour une seule machine. Pour gérer des charges de travail volumineuses, Cloud Firestore partitionne les données en éléments distincts qui peuvent être stockés sur et servis à partir de plusieurs machines ou serveurs de stockage . Ces partitions sont réalisées sur les tables de la base de données en blocs de plages de clés appelés splits.

Réplication synchrone

Il est important de noter que la base de données est toujours répliquée automatiquement et de manière synchrone. Les divisions de données ont des répliques dans différentes zones pour les garder disponibles même lorsqu'une zone devient inaccessible. La réplication cohérente sur les différentes copies du fractionnement est gérée par l'algorithme Paxos pour le consensus. Une réplique de chaque division est élue pour agir en tant que leader Paxos, responsable de la gestion des écritures dans cette division. La réplication synchrone vous donne la possibilité de toujours pouvoir lire la dernière version des données de Cloud Firestore.

Le résultat global est un système évolutif et hautement disponible qui offre de faibles latences pour les lectures et les écritures, quelles que soient les charges de travail lourdes et à très grande échelle.

Disposition des données

Cloud Firestore est une base de données de documents sans schéma. Cependant, en interne, il présente les données principalement dans deux tables de style base de données relationnelles dans sa couche de stockage comme suit :

  • Table des documents : les documents sont stockés dans cette table.
  • Table des index : Les entrées d'index permettant d'obtenir des résultats efficacement et triées par valeur d'index sont stockées dans cette table.

Le diagramme suivant montre à quoi pourraient ressembler les tables d'une base de données Cloud Firestore avec les fractionnements. Les divisions sont répliquées dans trois zones différentes et chaque division a un chef Paxos assigné.

Disposition des données

Région unique ou multirégion

Lorsque vous créez une base de données, vous devez sélectionner une région ou multi-régions .

Un emplacement régional unique est un emplacement géographique spécifique, comme us-west1 . Les répartitions de données d'une base de données Cloud Firestore ont des réplicas dans différentes zones de la région sélectionnée, comme expliqué précédemment.

Un emplacement multirégional consiste en un ensemble défini de régions où sont stockées les répliques de la base de données. Dans un déploiement multirégional de Cloud Firestore, deux des régions disposent de réplicas complets de l'intégralité des données de la base de données. Une troisième région dispose d'un réplica témoin qui ne conserve pas un ensemble complet de données, mais participe à la réplication. En répliquant les données entre plusieurs régions, les données peuvent être écrites et lues même en cas de perte d'une région entière.

Pour plus d'informations sur les emplacements d'une région, consultez Emplacements Cloud Firestore .

Région unique versus multirégion

Comprendre la durée de vie d'une écriture dans Cloud Firestore

Un client Cloud Firestore peut écrire des données en créant, mettant à jour ou supprimant un seul document. Une écriture dans un seul document nécessite la mise à jour atomique du document et de ses entrées d'index associées dans la couche de stockage. Cloud Firestore prend également en charge les opérations atomiques consistant en plusieurs lectures et/ou écritures sur un ou plusieurs documents.

Pour tous les types d'écritures, Cloud Firestore fournit les propriétés ACID (atomicité, cohérence, isolation et durabilité) des bases de données relationnelles. Cloud Firestore offre également la sérialisabilité , ce qui signifie que toutes les transactions apparaissent comme si elles étaient exécutées dans un ordre en série.

Étapes de haut niveau dans une transaction d'écriture

Lorsque le client Cloud Firestore émet une écriture ou valide une transaction, à l'aide de l'une des méthodes mentionnées précédemment, celle-ci est exécutée en interne sous la forme d'une transaction de lecture-écriture de base de données dans la couche de stockage. La transaction permet à Cloud Firestore de fournir les propriétés ACID mentionnées précédemment.

Lors de la première étape d'une transaction, Cloud Firestore lit le document existant et détermine les mutations à apporter aux données de la table Documents.

Cela inclut également les mises à jour nécessaires de la table Index comme suit :

  • Les champs ajoutés aux documents nécessitent des insertions correspondantes dans le tableau Index.
  • Les champs qui sont supprimés des documents nécessitent des suppressions correspondantes dans le tableau Index.
  • Les champs en cours de modification dans les documents nécessitent à la fois des suppressions (pour les anciennes valeurs) et des insertions (pour les nouvelles valeurs) dans la table Index.

Pour calculer les mutations mentionnées précédemment, Cloud Firestore lit la configuration d'indexation du projet. La configuration d'indexation stocke des informations sur les index d'un projet. Cloud Firestore utilise deux types d'index : à champ unique et composite. Pour une compréhension détaillée des index créés dans Cloud Firestore, consultez Types d'index dans Cloud Firestore .

Une fois les mutations calculées, Cloud Firestore les collecte dans une transaction puis la valide.

Comprendre une transaction d'écriture dans la couche de stockage

Comme indiqué précédemment, une écriture dans Cloud Firestore implique une transaction de lecture-écriture dans la couche de stockage. Selon la disposition des données, une écriture peut impliquer une ou plusieurs divisions, comme le montre la disposition des données .

Dans le diagramme suivant, la base de données Cloud Firestore comporte huit divisions (marquées de 1 à 8) hébergées sur trois serveurs de stockage différents dans une seule zone, et chaque division est répliquée dans 3 zones différentes (ou plus). Chaque division a un chef Paxos, qui peut se trouver dans une zone différente pour différentes divisions.

Division de la base de données Cloud Firestore

Prenons l'exemple d'une base de données Cloud Firestore contenant la collection Restaurants comme suit :

Collection de restaurants

Le client Cloud Firestore demande la modification suivante à un document de la collection Restaurant en mettant à jour la valeur du champ priceCategory .

Passer à un document en collection

Les étapes générales suivantes décrivent ce qui se passe dans le cadre de l'écriture :

  1. Créez une transaction de lecture-écriture.
  2. Lisez le document restaurant1 dans la collection Restaurants à partir de la table Documents de la couche de stockage.
  3. Lisez les index du document dans la table Index .
  4. Calculez les mutations à apporter aux données. Dans ce cas, il y a cinq mutations :
    • M1 : mettez à jour la ligne du restaurant1 dans la table Documents pour refléter le changement de valeur du champ priceCategory .
    • M2 et M3 : supprimez les lignes de l'ancienne valeur de priceCategory dans la table Index pour les index décroissants et ascendants.
    • M4 et M5 : insérez les lignes pour la nouvelle valeur de priceCategory dans la table Index pour les index décroissants et ascendants.
  5. Commettez ces mutations.

Le client de stockage du service Cloud Firestore recherche les divisions qui possèdent les clés des lignes à modifier. Considérons un cas où Split 3 dessert M1 et Split 6 dessert M2-M5. Il existe une transaction distribuée, impliquant toutes ces divisions en tant que participants . Les divisions de participants peuvent également inclure toute autre division à partir de laquelle des données ont été lues précédemment dans le cadre de la transaction de lecture-écriture.

Les étapes suivantes décrivent ce qui se passe dans le cadre de la validation :

  1. Le client de stockage émet une validation. Le commit contient les mutations M1-M5.
  2. Les splits 3 et 6 sont les participants à cette transaction. L'un des participants est choisi comme coordinateur , comme Split 3. Le travail du coordinateur est de s'assurer que la transaction est validée ou abandonnée de manière atomique pour tous les participants.
    • Les répliques leaders de ces divisions sont responsables du travail effectué par les participants et les coordinateurs.
  3. Chaque participant et coordinateur exécute un algorithme Paxos avec leurs répliques respectives.
    • Le leader exécute un algorithme Paxos avec les répliques. Le quorum est atteint si la plupart des réplicas répondent par une réponse ok to commit au leader.
    • Chaque participant prévient ensuite le coordinateur lorsqu'il est prêt (première phase d'engagement en deux phases). Si un participant ne peut pas valider la transaction, la transaction entière aborts .
  4. Une fois que le coordinateur sait que tous les participants, y compris lui-même, sont préparés, il communique le résultat de la transaction accept à tous les participants (deuxième phase de validation en deux phases). Dans cette phase, chaque participant enregistre la décision de validation dans un stockage stable et la transaction est validée.
  5. Le coordinateur répond au client de stockage dans Cloud Firestore que la transaction a été validée. En parallèle, le coordinateur et tous les participants appliquent les mutations aux données.

Valider le cycle de vie

Lorsque la base de données Cloud Firestore est petite, il peut arriver qu'un seul split possède toutes les clés des mutations M1-M5. Dans un tel cas, il n’y a qu’un seul participant à la transaction et la validation en deux phases mentionnée précédemment n’est pas requise, ce qui accélère les écritures.

Écrit en multi-région

Dans un déploiement multirégional, la répartition des réplicas entre les régions augmente la disponibilité, mais entraîne un coût en termes de performances. La communication entre les réplicas de différentes régions prend des temps d’aller-retour plus longs. Par conséquent, la latence de base pour les opérations Cloud Firestore est légèrement supérieure à celle des déploiements dans une seule région.

Nous configurons les réplicas de manière à ce que le leadership des divisions reste toujours dans la région principale. La région principale est celle à partir de laquelle le trafic arrive vers le serveur Cloud Firestore. Cette décision de la direction réduit le délai aller-retour dans la communication entre le client de stockage dans Cloud Firestore et le responsable de la réplication (ou le coordinateur des transactions multi-split).

Chaque écriture dans Cloud Firestore implique également une certaine interaction avec le moteur temps réel de Cloud Firestore. Pour plus d'informations sur les requêtes en temps réel, consultez Comprendre les requêtes en temps réel à grande échelle .

Comprendre la durée de vie d'une lecture dans Cloud Firestore

Cette section examine les lectures autonomes et non en temps réel dans Cloud Firestore. En interne, le serveur Cloud Firestore gère la plupart de ces requêtes en deux étapes principales :

  1. Une analyse de plage unique sur la table Index
  2. Recherches de points dans la table Documents en fonction du résultat de l'analyse précédente
Certaines requêtes peuvent nécessiter moins ou plus de traitement (par exemple, les requêtes IN) dans Cloud Firestore.

Les lectures de données à partir de la couche de stockage sont effectuées en interne à l'aide d'une transaction de base de données pour garantir des lectures cohérentes. Cependant, contrairement aux transactions utilisées pour les écritures, ces transactions ne prennent pas de verrou. Au lieu de cela, ils fonctionnent en choisissant un horodatage, puis en exécutant toutes les lectures à cet horodatage. Puisqu’ils n’acquièrent pas de verrous, ils ne bloquent pas les transactions simultanées en lecture-écriture. Pour exécuter cette transaction, le client de stockage dans Cloud Firestore spécifie une limite d'horodatage, qui indique à la couche de stockage comment choisir un horodatage de lecture. Le type de limite d'horodatage choisi par le client de stockage dans Cloud Firestore est déterminé par les options de lecture de la requête de lecture.

Comprendre une transaction de lecture dans la couche de stockage

Cette section décrit les types de lectures et la manière dont elles sont traitées dans la couche de stockage dans Cloud Firestore.

Des lectures fortes

Par défaut, les lectures Cloud Firestore sont fortement cohérentes . Cette forte cohérence signifie qu'une lecture Cloud Firestore renvoie la dernière version des données qui reflète toutes les écritures validées jusqu'au début de la lecture.

Lecture fractionnée unique

Le client de stockage dans Cloud Firestore recherche les divisions qui possèdent les clés des lignes à lire. Supposons qu'il doive effectuer une lecture à partir du Split 3 de la section précédente. Le client envoie la demande de lecture au réplica le plus proche pour réduire la latence aller-retour.

À ce stade, les cas suivants peuvent se produire en fonction de la réplique choisie :

  • La demande de lecture est envoyée à une réplique leader (Zone A).
    • Comme le leader est toujours à jour, la lecture peut se dérouler directement.
  • La demande de lecture est envoyée à une réplique non leader (telle que la zone B)
    • Le fractionnement 3 peut savoir, par son état interne, qu'il dispose de suffisamment d'informations pour servir la lecture et le fractionnement le fait.
    • Split 3 ne sait pas s'il a vu les dernières données. Il envoie un message au leader pour demander l'horodatage de la dernière transaction qu'il doit appliquer pour servir la lecture. Une fois cette transaction appliquée, la lecture peut continuer.

Cloud Firestore renvoie ensuite la réponse à son client.

Lecture multi-fractionnée

Dans la situation où les lectures doivent être effectuées à partir de plusieurs divisions, le même mécanisme se produit dans toutes les divisions. Une fois les données renvoyées de toutes les divisions, le client de stockage dans Cloud Firestore combine les résultats. Cloud Firestore répond ensuite à son client avec ces données.

Lectures périmées

Les lectures fortes sont le mode par défaut dans Cloud Firestore. Cependant, cela a un coût en termes de latence potentielle plus élevée en raison de la communication qui peut être nécessaire avec le leader. Souvent, votre application Cloud Firestore n'a pas besoin de lire la dernière version des données et la fonctionnalité fonctionne bien avec des données qui peuvent être obsolètes depuis quelques secondes.

Dans un tel cas, le client peut choisir de recevoir des lectures obsolètes en utilisant les options de lecture read_time . Dans ce cas, les lectures sont effectuées lorsque les données se trouvaient à read_time , et il est fort probable que la réplique la plus proche ait déjà vérifié qu'elle contient des données à read_time spécifiée. Pour des performances sensiblement meilleures, 15 secondes constituent une valeur d’obsolescence raisonnable. Même pour les lectures obsolètes, les lignes générées sont cohérentes les unes avec les autres.

Évitez les points chauds

Les divisions dans Cloud Firestore sont automatiquement divisées en morceaux plus petits pour répartir le travail de desserte du trafic vers davantage de serveurs de stockage en cas de besoin ou lorsque l'espace clé s'agrandit. Les fractionnements créés pour gérer le trafic excédentaire sont conservés pendant environ 24 heures, même si le trafic disparaît. Ainsi, s’il y a des pics de trafic récurrents, les répartitions sont maintenues et d’autres répartitions sont introduites chaque fois que nécessaire. Ces mécanismes aident les bases de données Cloud Firestore à évoluer automatiquement en cas d'augmentation de la charge de trafic ou de la taille de la base de données. Cependant, il y a certaines limites à prendre en compte, comme expliqué ci-dessous.

La répartition du stockage et de la charge prend du temps, et une augmentation trop rapide du trafic peut entraîner des erreurs de latence élevée ou de dépassement de délai, communément appelées points d'accès , pendant que le service s'ajuste. La meilleure pratique consiste à répartir les opérations sur la plage de clés, tout en augmentant le trafic sur une collection dans une base de données à 500 opérations par seconde. Après cette montée en puissance progressive, augmentez le trafic jusqu'à 50 % toutes les cinq minutes. Ce processus est appelé règle 500/50/5 et positionne la base de données pour qu'elle s'adapte de manière optimale à votre charge de travail.

Bien que les divisions soient créées automatiquement avec l'augmentation de la charge, Cloud Firestore ne peut diviser une plage de clés que jusqu'à ce qu'il serve un seul document à l'aide d'un ensemble dédié de serveurs de stockage répliqués. Par conséquent, des volumes élevés et soutenus d’opérations simultanées sur un seul document peuvent conduire à l’apparition d’un point chaud sur ce document. Si vous rencontrez des latences élevées et soutenues sur un seul document, vous devez envisager de modifier votre modèle de données pour diviser ou répliquer les données sur plusieurs documents.

Des erreurs de contention se produisent lorsque plusieurs opérations tentent de lire et/ou d’écrire simultanément le même document.

Un autre cas particulier de hotspotting se produit lorsqu'une clé augmentant/diminuant séquentiellement est utilisée comme ID de document dans Cloud Firestore, et qu'il y a un nombre considérablement élevé d'opérations par seconde. Créer davantage de divisions n'aide pas ici puisque l'augmentation du trafic se déplace simplement vers la division nouvellement créée. Étant donné que Cloud Firestore indexe automatiquement tous les champs du document par défaut, de tels points chauds mobiles peuvent également être créés sur l'espace d'index pour un champ de document contenant une valeur croissante/décroissante séquentiellement, comme un horodatage.

Notez qu'en suivant les pratiques décrites ci-dessus, Firestore peut évoluer pour gérer des charges de travail arbitrairement importantes sans que vous ayez à ajuster la configuration.

Dépannage

Firestore fournit Key Visualizer en tant qu'outil de diagnostic spécialement conçu pour analyser les modèles d'utilisation et résoudre les problèmes de hotspotting.

Et après