NoSQL distribué avec prise en charge des transactions - FoundationDB

[Introduction] Temps de lecture du week-end, un bon article (https://cacm.acm.org/magazines/2023/6/273229-foundationdb-a-distributed-key-value-store/fulltext) a élargi mes horizons, NoSQL qui prend en charge la sémantique des transactions devrait être incluse dans les alternatives d’architecture de système logiciel.

FoundationDB est un système de stockage transactionnel clé-valeur open source et l'un des premiers systèmes à combiner la flexibilité et l'évolutivité de l'architecture NoSQL avec les performances puissantes des transactions ACID. L'architecture FoundationDB est découplée en un système de gestion des transactions en mémoire, un système de stockage distribué et un système de configuration distribué intégré. Chaque sous-système peut être configuré indépendamment pour l'évolutivité, la haute disponibilité et la tolérance aux pannes.

FoundationDB comprend également un cadre de simulation déterministe pour tester de nouvelles fonctionnalités dans des conditions de défaillance possibles. Ces tests rigoureux rendent FoundationDB plus stable et permettent aux développeurs d'introduire et de publier de nouvelles fonctionnalités à un rythme rapide.

Dans le même temps, FoundationDB fournit un ensemble minimal de fonctionnalités soigneusement sélectionnées pour créer différents systèmes sur FoundationDB. La cohérence, la robustesse et la disponibilité de ses données en font la base de l'infrastructure cloud d'Apple, Snowflake et d'autres sociétés, utilisée pour stocker les données utilisateur, les métadonnées et la configuration du système, ainsi que d'autres informations critiques.

1. Informations générales

1.1 Solutions NoSQL actuelles et problèmes rencontrés

De nombreux services cloud s'appuient sur des backends de stockage distribués évolutifs pour conserver l'état des applications. De tels systèmes de stockage doivent être tolérants aux pannes et hautement disponibles, tout en fournissant une sémantique et un modèle de données suffisamment flexibles pour permettre un développement rapide d'applications. Ces services doivent pouvoir s'adapter à des milliards d'utilisateurs, stocker des pétaoctets ou des exaoctets de données et traiter des millions de requêtes par seconde.

L'émergence des systèmes NoSQL facilite le développement d'applications, facilite l'extension et l'exploitation des systèmes de stockage, offre une tolérance aux pannes et prend en charge divers modèles de données. Dans un souci d'évolutivité, ces systèmes NoSQL sacrifient la sémantique des transactions et assurent une éventuelle cohérence des données, obligeant les développeurs d'applications à considérer la question des mises à jour des données pour les opérations simultanées.

b5dcd1f6e7ba3e3c7ade09fd53a3b7e4.png

1.2 Origine et caractéristiques de FoundationDB

FoundationDB a été créée en 2009 dans l'espoir de devenir le système fondamental nécessaire à la construction de systèmes distribués avancés. Il s’agit d’un magasin clé-valeur ordonné, transactionnel, qui prend en charge de manière native les transactions multi-clés strictement sérialisées sur l’ensemble de son espace clé. Il fournit un moteur de stockage transactionnel hautement évolutif avec un ensemble minimal de fonctionnalités soigneusement sélectionnées. Il ne fournit pas de sémantique structurée, de langages de requête, de modèles de données, d'index secondaires ou de nombreuses autres fonctionnalités généralement trouvées dans les bases de données transactionnelles.

Le modèle NoSQL offre aux développeurs d'applications une grande flexibilité. Les applications peuvent stocker des données sous forme de simples paires clé-valeur, mais doivent implémenter des fonctionnalités plus avancées telles que des index secondaires cohérents et des contrôles d'intégrité référentielle. FoundationDB utilise par défaut des transactions strictement sérialisables, mais permet d'assouplir cette sémantique sous un contrôle précis pour s'adapter aux applications qui ne nécessitent pas de telles transactions.

L'une des raisons de la popularité de FoundationDB et de la communauté open source croissante est qu'elle se concentre sur la « moitié inférieure » de la base de données, laissant le reste aux applications sans état situées au-dessus pour fournir divers modèles de données et autres fonctionnalités. Les différentes couches construites sur FoundationDB démontrent l’utilité de cette conception inhabituelle. Par exemple, la couche d'enregistrement FoundationDB ajoute la plupart de ce que les utilisateurs attendent d'une base de données relationnelle, et la base de données graphique JanusGraph fournit une implémentation basée sur la couche FoundationDB. CouchDB est en cours de reconstruction en tant que couche sur FoundationDB. Par conséquent, les applications traditionnelles peuvent également utiliser FoundationDB.

Tester et déboguer des systèmes distribués est tout aussi difficile que de les construire. Des pannes inattendues de processus et de réseau, la réorganisation des messages et d'autres sources de non-déterminisme peuvent révéler des hypothèses implicites qui ne correspondent pas à la réalité, rendant ces bogues extrêmement difficiles à reproduire ou à déboguer. Ces erreurs sont souvent fatales aux systèmes de bases de données explicites. De plus, la nature dynamique des systèmes de bases de données signifie qu’une telle erreur pourrait entraîner une corruption des données, mais sa détection pourrait prendre des mois. Les techniques de vérification de modèle peuvent vérifier l’exactitude des protocoles distribués, mais ne peuvent souvent pas vérifier la mise en œuvre réelle. Les vulnérabilités profondes, qui se produisent uniquement lorsque plusieurs crashs se produisent dans une séquence spécifique, constituent un défi pour les tests de bout en bout.

FoundationDB adopte une approche radicale : avant de créer la base de données elle-même, il construit un cadre de simulation de base de données déterministe capable de simuler un réseau de processus en interaction et diverses pannes et récupérations de disque, de processus, de réseau et de requête, le tout étant réalisé au sein d'un seul site physique. processus. L'extension de syntaxe C++ Flow a été créée spécifiquement à cet effet. Ces tests rigoureux en simulation rendent FoundationDB très stable et permettent à ses développeurs d'introduire de nouvelles fonctionnalités et versions à un rythme rapide.

L'architecture faiblement couplée de FoundationDB se compose d'un plan de contrôle et d'un plan de données. Le plan de contrôle gère les métadonnées du cluster et utilise Active Disk Paxos pour la haute disponibilité. Le plan de données se compose d'un système de gestion des transactions et d'une couche de stockage distribuée. La première est responsable du traitement des mises à jour et la seconde est responsable de la fourniture des lectures ; les deux peuvent être étendus indépendamment. FoundationDB implémente une sérialisation stricte grâce à une combinaison de contrôle de concurrence optimiste et de contrôle de concurrence multiversion.

Une fonctionnalité qui distingue FoundationDB des autres bases de données distribuées est la façon dont elle gère les échecs. FoundationDB ne s'appuie pas sur un mécanisme de quorum, mais tente plutôt de détecter et de récupérer de manière proactive les pannes en reconfigurant le système. Cela nous permet d'atteindre le même niveau de tolérance aux pannes avec moins de ressources : FoundationDB peut tolérer n échecs tout en ne nécessitant que n+1 (au lieu de 2n+1) répliques. Cette méthode convient à un déploiement local ou régional. Fournit une nouvelle stratégie pour les déploiements WAN qui évite la latence d'écriture entre régions tout en assurant un basculement automatique entre les régions sans perte de données.

2. Principes de conception et architecture du système

Les principaux principes de conception de FoundationDB sont la division pour mieux régner, la conception orientée pannes et les tests de simulation .

FoundationDB dissocie le système de gestion des transactions (écritures) du système de stockage distribué (lectures) et les met à l'échelle indépendamment. Dans un système de gestion des transactions, les processus se voient attribuer divers rôles qui représentent différents aspects de la gestion des transactions. En outre, les tâches d'orchestration à l'échelle du cluster, telles que le contrôle des surcharges et l'équilibrage de charge, sont également divisées et exécutées par d'autres rôles différents.

Pour les systèmes distribués, l’échec est une nécessité plutôt qu’une exception. Afin de faire face aux pannes du système de gestion des transactions, toutes les pannes doivent être récupérées : lorsqu'une panne est détectée, le système de transaction est activement arrêté. En conséquence, toute gestion des échecs est réduite à une seule opération de récupération, qui devient un chemin de code commun et bien testé. Pour améliorer la disponibilité, FoundationDB s'efforce de minimiser le temps moyen de récupération (MTTR). Dans notre cluster de production, la durée totale est généralement inférieure à cinq secondes.

FoundationDB s'appuie sur un cadre de test de simulation stochastique et déterministe pour tester l'exactitude de sa base de données distribuée. Le cadre de tests simulés expose non seulement des bogues profonds, mais améliore également la productivité des développeurs et la qualité du code de FoundationDB.

2.1.Architecture

Le cluster FoundationDB dispose d'un plan de contrôle pour gérer les métadonnées système critiques et l'orchestration à l'échelle du cluster, ainsi que d'un plan de données pour le traitement des transactions et le stockage des données, comme le montre la figure ci-dessous.

f8d85d0d5d6a637b835f3bfd832cf515.jpeg

avion de contrôle

Le plan de contrôle est responsable de la persistance des métadonnées clés du système (c'est-à-dire la configuration du système de transaction) sur le coordinateur. Ces coordinateurs forment un groupe Paxos et élisent un contrôleur de cluster. Le contrôleur de cluster surveille tous les serveurs du cluster et gère trois processus : le séquenceur, le distributeur de données et le contrôleur de débit. S'ils échouent ou se bloquent, ces processus sont redémarrés. Le distributeur de données est responsable de la surveillance des pannes et de l'équilibrage des données entre les serveurs de stockage. Le contrôleur de débit fournit une protection contre les surcharges au cluster.

plan de données

FoundationDB convient aux charges de travail OLTP qui lisent plus et écrivent moins, lisent et écrivent un petit nombre de mots-clés par transaction et nécessitent une évolutivité. Le système de gestion de transactions distribuées se compose de séquenceurs, d'agents et de résolveurs à l'échelle de la partition, qui sont tous des processus sans état. Le système de journalisation stocke le journal d'écriture avant de TS, tandis qu'un système de stockage distribué distinct est utilisé pour stocker les données et fournir des services de lecture. Un système de journalisation se compose d'un ensemble de serveurs de journaux, tandis qu'un système de stockage distribué dispose de plusieurs serveurs de stockage. Le séquenceur attribue des versions de lecture et de validation à chaque transaction. Le courtier fournit des lectures multiversions aux clients et coordonne les validations de transactions. L'analyseur vérifie les conflits entre les transactions. Le serveur de journaux agit comme une file d'attente persistante répliquée, fragmentée et distribuée, chaque file d'attente stockant les données WAL pour un serveur de stockage. Un système de stockage distribué se compose de plusieurs serveurs de stockage. Chaque serveur de stockage stocke un ensemble de fragments de données, c'est-à-dire une plage de clés continue, et permet aux clients d'y accéder. Les serveurs de stockage représentent la majorité des processus du système et forment ensemble un arbre B distribué. Le moteur de stockage sur chaque serveur de stockage est une version améliorée de SQLite, avec des améliorations accélérant la suppression des plages, reportant les suppressions aux tâches en arrière-plan et ajoutant la prise en charge de la programmation asynchrone.

2.1.1 Séparation et développement de la lecture et de l'écriture

Les processus ci-dessus sont attribués à différents rôles et étendus en ajoutant de nouveaux processus pour chaque rôle. Les clients lisent à partir de serveurs de stockage partitionnés, les lectures évoluent donc de manière linéaire avec le nombre de serveurs de stockage. Faites évoluer l'écriture en ajoutant davantage de proxys, d'analyseurs et de serveurs de journaux. Les processus singleton du plan de contrôle (tels que le contrôleur de cluster et le séquenceur) et le coordinateur ne constituent pas des goulots d'étranglement en termes de performances ; ils effectuent uniquement des opérations limitées sur les métadonnées.

2.1.2 Démarrage au démarrage

FoundationDB n'a aucune dépendance vis-à-vis des services de coordination externes. Toutes les données utilisateur et la plupart des métadonnées système sont stockées sur le serveur de stockage. Les métadonnées sur le serveur de stockage sont stockées dans le serveur de journaux et les données de configuration du serveur de journaux sont stockées dans tous les coordinateurs. Le coordinateur est un groupe Paxos de disque ; si aucun contrôleur de cluster n'existe, le serveur tente de devenir un contrôleur de cluster. Le contrôleur de cluster nouvellement élu lit l'ancienne configuration LS du coordinateur et génère de nouveaux serveurs de transactions et serveurs de journaux. L'agent restaure les métadonnées système de l'ancien LS, y compris les informations sur tous les serveurs de stockage. Le séquenceur attend que le nouveau serveur de transactions termine la récupération, puis écrit la nouvelle configuration du serveur de journaux sur tous les coordinateurs. Le nouveau système de transactions est alors prêt à recevoir les transactions des clients.

2.1.3 Reconfiguration

Le processus séquenceur surveille l'état des agents, des analyseurs et des serveurs de journaux. Le séquenceur se terminera chaque fois que le serveur de journaux ou le serveur de journaux tombe en panne, ou que la configuration de la base de données change. Le contrôleur de cluster détecte la panne du séquenceur, démarre et amorce le nouveau serveur de transactions et le nouveau serveur de journaux. De cette manière, le traitement des transactions est divisé en époques, chaque époque représentant la génération d'un système de gestion de transactions avec son propre séquenceur.

2.2. Gestion des transactions

2.2.1 Traitement des transactions de bout en bout

Les transactions client obtiennent d’abord une version lue (c’est-à-dire un horodatage) en contactant l’un des courtiers. L'agent demande ensuite au séquenceur de générer une version lue au moins aussi bonne que la version de validation de toutes les transactions précédemment émises et renvoie cette version lue au client. Le client peut ensuite émettre une lecture sur le serveur de stockage et obtenir la valeur sous une version de lecture spécifique. Les écritures du client sont mises en cache localement sans contacter le cluster, et les résultats de la recherche dans la base de données de la transaction sont combinés avec les écritures non validées pour préserver les lectures. Lors de la validation, le client envoie les données de transaction à l'un des courtiers et attend une réponse de validation ou d'abandon. Si la transaction échoue, le client peut choisir de la redémarrer.

L'agent valide les transactions client en trois étapes. Tout d’abord, il contacte le séquenceur pour obtenir une version de validation supérieure à toutes les versions de lecture ou de validation existantes. Le séquenceur sélectionne les versions validées à une fréquence pouvant atteindre 1 million de versions par seconde. Le courtier envoie ensuite les informations de transaction au résolveur de plage de partition, qui implémente le contrôle de concurrence optimiste de FoundationDB en vérifiant les conflits de lecture et d'écriture. Si tous les résolveurs sont exempts de conflits, la transaction peut entrer dans la phase de validation finale. Sinon, l'agent marque la transaction comme abandonnée. Enfin, les transactions validées sont envoyées à un ensemble de serveurs de journaux pour assurer leur persistance. Une fois que tous les serveurs de journaux désignés ont répondu au courtier, la transaction est considérée comme validée et le courtier signale la version validée au séquenceur puis répond au client. Le serveur de stockage extrait en permanence les journaux de mutation du serveur de journaux et applique les mises à jour validées sur le disque.

En plus des transactions en lecture-écriture mentionnées ci-dessus , FoundationDB prend également en charge les transactions en lecture seule et les lectures d'instantanés. Les transactions en lecture seule sont à la fois sérialisables (se produisant lors de la lecture de la version) et efficaces, et le client peut fonctionner sans contacter la base de données. . Soumettez ces transactions localement. Les lectures d'instantanés dans FoundationDB assouplissent sélectivement les propriétés d'isolation des transactions en réduisant les conflits, c'est-à-dire que les écritures simultanées n'entrent pas en conflit avec les lectures d'instantanés.

2.2.2 Sérialisation stricte

FoundationDB implémente l'isolation des instantanés sérialisables en combinant un contrôle de concurrence optimisé avec un contrôle multi-version. Rappelez-vous que la transaction Tx obtient sa version de lecture et sa version de validation du séquenceur, où le numéro de version de lecture est garanti comme n'étant pas inférieur à n'importe quelle version de validation au démarrage de Tx , et la version de validation est supérieure à tout numéro de version de lecture ou de validation existant. Cette version de validation définit l'historique en série de la transaction et est utilisée comme numéro de séquence du journal (LSN). Étant donné que Tx observe les résultats de toutes les transactions précédemment validées, FoundationDB implémente une sérialisation stricte. Pour garantir qu'il n'y a pas d'espace entre les numéros de séquence du journal, le séquenceur renvoie la validation précédente dans chaque validation. Le courtier envoie le LSN et le LSN précédent à l'analyseur et au serveur de journaux afin qu'ils puissent traiter les transactions en série dans l'ordre du LSN.

De même, le serveur de stockage extrait les données de journal du serveur de journal dans l'ordre LSN croissant. Le résolveur de plage de partition utilise un algorithme de détection de conflit sans verrouillage similaire à l'isolation d'instantané en écriture , sauf que la détection de conflit se produit avant que la version validée ne soit sélectionnée dans FoundationDB. Cela permet à FoundationDB de gérer efficacement par lots l’allocation de versions et la détection des conflits. L'ensemble de l'espace clé est divisé entre les résolveurs de plages de partitions, ce qui permet d'effectuer la détection des conflits en parallèle. Une transaction ne peut être validée que si tous les résolveurs à l'échelle de la partition accusent réception de la transaction. Sinon, la transaction sera annulée. Il est possible qu'une transaction interrompue ait été reconnue par un sous-ensemble de résolveurs à l'échelle de la partition, et qu'ils aient mis à jour l'historique des transactions potentiellement validées, ce qui pourrait provoquer un conflit avec d'autres transactions (c'est-à-dire des faux positifs).

En pratique, cela ne pose pas de problème pour les charges de travail de production, car la plage de clés de la transaction appartient généralement à un résolveur de plage de partitions. De plus, étant donné que la clé modifiée expire après la fenêtre de multiversion, un tel faux positif ne se produira que pendant la courte période de la fenêtre de multiversion (c'est-à-dire 5 secondes). Le mécanisme de conception optimisé du contrôle de concurrence de FoundationDB évite la logique complexe d'acquisition et de libération des verrous, simplifiant considérablement l'interaction entre les services de transaction et les services de stockage. Le compromis est que les transactions avortées gaspillent du travail.

Dans les charges de travail de production multi-tenant, les taux de conflits de transactions sont très faibles (moins de 1 %) et le contrôle optimisé de la concurrence fonctionne bien. Si un conflit survient, le client peut simplement redémarrer la transaction.

2.2.3 Protocole de journalisation

Une fois que le courtier décide de valider la transaction, un message est envoyé à tous les serveurs de journaux : les modifications sont envoyées au serveur de journaux chargé de modifier la plage de clés, tandis que les autres serveurs de journaux reçoivent un corps de message vide. L'en-tête du message de journal inclut le LSN actuel et précédent obtenu à partir du séquenceur ainsi que la version de validation maximale connue pour cet agent. Une fois que le serveur de journaux a rendu les données de journal persistantes, il répond à l'agent. Si tous les serveurs de journaux réplicas ont répondu et que ce LSN est supérieur au KCV actuel, l'agent met à jour son KCV au LSN et envoie les journaux redo du LS à Le serveur ne fait pas partie du chemin de soumission, mais est effectué en arrière-plan.

Dans FoundationDB, le serveur de stockage applique les journaux redo non persistants du serveur de journaux à l'index en mémoire. En règle générale, cela se produit avant que les versions de lecture validées réfléchies ne soient distribuées au client, ce qui permet de diffuser des lectures multiversions avec une latence très faible. Par conséquent, lorsqu'une demande de lecture client atteint le serveur de stockage, la version demandée (c'est-à-dire les dernières données validées) est généralement déjà disponible. S'il n'y a aucune nouvelle donnée lisible sur la réplique du serveur de stockage, le client attend que les données soient disponibles ou réémet la demande sur une autre réplique.

Si les deux expirent, le client peut simplement redémarrer la transaction. Étant donné que les données des journaux sont déjà conservées sur le serveur de journaux, le serveur de stockage peut mettre les mises à jour en mémoire tampon et conserver les données sur le disque par lots à intervalles réguliers, améliorant ainsi l'efficacité des E/S.

2.2.4 Récupération du système de transactions

Les systèmes de bases de données traditionnels utilisent généralement le protocole de récupération ARIES. Pendant la récupération, le système traite les enregistrements de journal du point de contrôle précédent en réappliquant les enregistrements de journalisation aux pages de données pertinentes. Cela ramène la base de données à un état cohérent ; les transactions effectuées pendant le crash peuvent être annulées en effectuant une journalisation d'annulation. Dans FoundationDB, la récupération est délibérément conçue pour être très bon marché : il n'est pas nécessaire d'appliquer des entrées de journal d'annulation. Cela est dû à un choix de conception extrêmement simplifié : le traitement du journal redo est le même que le chemin de transfert de journal normal. 

Dans FoundationDB, le serveur de stockage extrait les journaux du serveur de journaux et les applique en arrière-plan. Le processus de récupération commence par la détection de la panne et le recrutement de nouveaux systèmes de transaction. Le nouveau TS peut accepter des transactions avant que toutes les données de l'ancien serveur de journaux n'aient été traitées. La récupération nécessite simplement de trouver la fin du journal redo : à ce stade (comme lors d'une opération de transfert normale), le serveur de stockage relit le journal de manière asynchrone.

Pour chaque époque, le contrôleur de cluster effectue une récupération en plusieurs étapes. Tout d'abord, il lit la configuration TS précédente auprès du coordinateur et verrouille ces informations pour empêcher une autre récupération simultanée. Ensuite, il restaure l'état précédent du système TS, y compris les informations sur les anciens serveurs de journaux, les empêche d'accepter des transactions et recrute un nouvel ensemble de séquenceurs, d'agents, d'analyseurs et de serveurs de journaux. Une fois le serveur de journaux précédent arrêté et le nouveau serveur de transactions démarré, le contrôleur de cluster écrit les informations du nouveau serveur de transactions sur le coordinateur. Les proxys et les résolveurs étant apatrides, leur récupération ne nécessite aucun travail supplémentaire. Au lieu de cela, le serveur de journaux conserve un journal des transactions validées, et nous devons nous assurer que toutes ces transactions sont durables et récupérables par le serveur de stockage. L'essence de la restauration de l'ancien serveur de journaux est de déterminer la fin du journal redo, c'est-à-dire la version de récupération (RV). Le déploiement des journaux d'annulation supprime essentiellement toutes les données après RV dans l'ancien serveur de journaux et le serveur de stockage. La figure 2 illustre comment RV est déterminé par le séquenceur.

4e21e3158adb86d5ee4155f1cfe7f7e6.jpeg

Les requêtes de l'agent au serveur de journaux seront superposées avec son KCV, qui est le plus grand LSN que cet agent a validé, et le LSN de la transaction en cours. Chaque serveur de journaux conserve le KCV maximum reçu et une version persistante, qui correspond au LSN maximum que le LogServer conserve. Pendant le processus de récupération, le séquenceur tente d'arrêter tous les anciens serveurs de journaux, et chaque réponse contient le DV et le KCV sur ce serveur de journaux. 

Supposons que le degré de réplication du serveur de journaux est k. Une fois que le séquenceur reçoit plus de mk réponses, le séquenceur sait que les transactions validées à l'époque précédente ont atteint la valeur maximale de tous les KCV, qui devient la version finale (PEV) de l'époque précédente. Toutes les données antérieures à cette version ont été entièrement répliquées. Pour l'époque actuelle, dont la version de départ est PEV +1, le séquenceur sélectionne la valeur minimale de tous les DV comme RV. Les journaux dans la plage [PEV + 1, RV] sont répliqués du serveur de journaux de l'époque précédente vers le serveur de journaux de l'époque actuelle pour réparer la réplication en cas de panne du serveur de journaux. La surcharge liée à la copie de cette plage est très faible car elle ne contient que quelques secondes de données de journal.

Lorsque le séquenceur accepte de nouvelles transactions, la première transaction est une transaction de récupération spéciale qui informe les serveurs de stockage de la valeur RV actuelle afin qu'ils puissent restaurer toutes les données plus volumineuses que la RV. Le moteur de stockage actuel de FoundationDB se compose d'un arbre B SQLite non versionné et de données de journalisation multiversion en mémoire. Seules les mutations qui quittent la fenêtre de multiversion (c'est-à-dire les données validées) sont écrites dans SQLite. La restauration supprime simplement les données multiversions en mémoire dans le serveur de stockage. Le serveur de stockage extrait ensuite du nouveau serveur de journaux toutes les données supérieures à la version PEV .

2.3. Copie

FoundationDB utilise diverses stratégies de réplication pour tolérer les échecs de différentes données.

2.3.1 Réplication des métadonnées

Les métadonnées système du plan de contrôle sont stockées sur le coordinateur, à l'aide d'Active Disk Paxos. Ces métadonnées peuvent être restaurées tant qu'une majorité (c'est-à-dire une majorité) de coordinateurs est active.

2.3.2 Réplication des journaux

Lorsque l'agent écrit des journaux sur le serveur de journaux, les enregistrements de journaux de chaque partition sont répliqués de manière synchrone sur k = f + 1 serveurs de journaux. Ce n'est qu'après toutes les k réponses avec persistance réussie que l'agent peut envoyer une réponse de validation au client. Une défaillance du serveur de journaux entraînera la récupération du système de transactions.

2.3.3 Réplication du stockage

Chaque fragment (c'est-à-dire plage de clés) est répliqué de manière asynchrone sur k = f + 1 serveurs de stockage, appelés équipe. Les serveurs de stockage hébergent généralement plusieurs fragments pour répartir uniformément leurs données entre plusieurs équipes. Une défaillance du serveur de stockage incite l'allocateur de données à déplacer les données de l'équipe contenant le processus défaillant vers d'autres équipes saines. Notez que l'abstraction de l'équipe de stockage est plus complexe que les Copysets.

Pour réduire la probabilité de perte de données due à des pannes simultanées, FoundationDB garantit qu'au plus un processus d'un groupe de réplicas se trouve dans le domaine de panne, tel qu'un hôte, un rack ou une zone de disponibilité. Chaque équipe est garantie d'avoir au moins un processus actif et aucune donnée ne sera perdue si l'un des domaines de pannes correspondants est encore disponible.

2.4 Essai de simulation

Tester et déboguer les systèmes distribués est une entreprise difficile et inefficace. Ce problème est particulièrement aigu pour FoundationDB : tout échec de son contrat de contrôle de concurrence fort peut causer des dommages presque arbitraires dans son système de couche supérieure. Par conséquent, une approche de test ambitieuse de bout en bout a été adoptée dès le départ : exécuter un logiciel de base de données réel dans des simulations déterministes à événements discrets, ainsi que des charges de travail synthétiques générées de manière aléatoire et une injection de fautes. L'environnement de simulation rigoureux déclenche rapidement des erreurs dans la base de données, et le déterminisme garantit que chacune de ces erreurs peut être reproduite et étudiée.

2.4.1 Simulateur déterministe

FoundationDB a intégré cette approche de test dès le début. Tout le code de la base de données est déterministe et évite la concurrence multithread (au lieu de cela, un nœud de base de données est déployé par cœur). La figure ci-dessous illustre le processus du simulateur FoundationDB, qui élimine toutes les sources de non-déterminisme et de communication, y compris les générateurs de réseau, de disque, de temps et de nombres pseudo-aléatoires. FoundationDB est écrit en Flow, une nouvelle extension de syntaxe C++ qui ajoute des primitives de concurrence asynchrone/de type attente et une annulation automatique, permettant à du code hautement concurrent de s'exécuter de manière déterministe. Flow fournit un modèle de programmation d'acteur, qui résume diverses opérations du processus serveur FoundationDB en plusieurs acteurs planifiés par la bibliothèque d'exécution Flow. Le processus de simulation peut générer plusieurs serveurs FoundationDB qui communiquent entre eux sur le réseau simulé dans une seule simulation d'événements discrets. L'implémentation de la production est un simple pont vers les appels système pertinents.

fe108f04160d0c905812f98bf9eff2c1.jpeg

L'émulateur exécute plusieurs charges de travail communiquant avec un serveur FoundationDB simulé sur un réseau simulé. Ces charges de travail incluent des instructions d'injection de pannes, des applications simulées, des modifications de configuration de base de données et des appels de fonctions internes de base de données. Les charges de travail sont composables pour tester diverses fonctionnalités et réutilisées pour créer des cas de test complets.

2.4.2 Agent de test

FoundationDB utilise divers agents de test pour détecter les échecs dans les simulations. La plupart des charges de travail synthétiques ont des assertions intégrées pour vérifier les contrats et les propriétés de la base de données, par exemple en vérifiant les invariants dans les données qui ne peuvent être conservés que par l'atomicité et l'isolement des transactions. Les assertions sont utilisées dans toute la base de code pour vérifier les propriétés qui peuvent être vérifiées « localement ». Des propriétés telles que la récupérabilité (disponibilité éventuelle) peuvent être vérifiées en ramenant l'environnement matériel modélisé (après un ensemble de pannes suffisantes pour détruire la disponibilité de la base de données) à un état à partir duquel la récupération devrait être possible et en vérifiant que le cluster finit par récupérer.

2.4.3 Injection de défauts

Simulez les pannes et les redémarrages des machines, des racks et des centres de données injectés, diverses pannes de réseau, les problèmes de partitionnement et de latence, le comportement du disque (tel que la corruption des écritures non synchronisées lors des redémarrages de la machine) et les événements aléatoires. Cette variété d'injections de fautes teste à la fois la résilience de la base de données à des fautes spécifiques et augmente la variété des états dans la simulation. La distribution de l'injection de fautes est soigneusement ajustée pour éviter des taux de défaillance excessifs qui feraient entrer le système dans un petit espace d'état. FoundationDB lui-même fonctionne avec le simulateur pour rendre les états et événements rares plus courants grâce à une technique avancée d'injection de fautes appelée de manière informelle « buggification ».

À de nombreux endroits de sa base de code, l'émulateur permet l'injection de comportements inhabituels (mais ne violant pas le contrat), tels que renvoyer inutilement une erreur lors d'une opération normalement réussie, injecter un délai sur une opération normalement rapide ou sélectionner une exception. Paramètres de réglage de la valeur, etc. Ceci est complété par une injection de fautes au niveau du réseau et du matériel. La randomisation des paramètres de réglage garantit également que des valeurs de réglage de performances spécifiques ne deviennent pas accidentellement nécessaires pour garantir l'exactitude. Les tests par essaim sont largement utilisés pour maximiser la diversité des simulations. Chaque exécution utilise une taille et une configuration de cluster aléatoires, une charge de travail aléatoire, des paramètres d'injection de fautes aléatoires, des paramètres de réglage aléatoires, et active et désactive un sous-ensemble aléatoire de points de buggification.

Nous disposons du cadre de test Swarm open source de FoundationDB. Des macros de couverture conditionnelle sont utilisées pour évaluer et ajuster l’efficacité des simulations. Par exemple, un développeur préoccupé par le fait que le nouveau code utilise rarement un tampon plein pourrait ajouter une ligne TEST(buffer.is_full()) ; l'analyse des résultats de la simulation lui indiquera combien d'exécutions de simulation différentes ont atteint cette condition. Si le nombre est trop faible ou nul, ils peuvent ajouter des capacités de bugification, de charge de travail ou d’injection de fautes pour garantir que le scénario est entièrement testé.

2.4.4 Retard dans la détection des erreurs

Trouver rapidement les bogues est important à la fois pour les rencontrer lors des tests avant la production et pour améliorer la productivité de l'ingénierie. Les bogues découverts immédiatement dans un seul commit peuvent être facilement retracés jusqu'à ce commit. Si l'utilisation du processeur à l'intérieur du simulateur est faible, une simulation d'événements discrets peut s'exécuter à une vitesse arbitraire car le simulateur peut avancer rapidement l'horloge jusqu'à l'événement suivant. De nombreux bogues de systèmes distribués prennent du temps à être découverts, et l'exécution dans une simulation avec de longues périodes de faible utilisation peut révéler davantage de ces bogues par cœur que les tests de bout en bout du « monde réel ». De plus, les tests aléatoires présentent un parallélisme embarrassant, et les développeurs de FoundationDB peuvent « exploser » le nombre de tests avant une version majeure dans l'espoir de détecter des bogues inhabituellement rares qui ont jusqu'à présent échappé au processus de test. Étant donné que l'espace de recherche est effectivement infini, l'exécution de davantage de tests permet de couvrir davantage de code et de détecter davantage de bogues potentiels, contrairement aux tests fonctionnels ou système scriptés.

2.4.5 Limites des tests de simulation

La simulation ne peut pas détecter de manière fiable les problèmes de performances, tels que des algorithmes d'équilibrage de charge imparfaits. Il ne peut pas non plus tester les bibliothèques ou dépendances tierces, ni même le code d'un tiers qui n'est pas implémenté dans Flow. Par conséquent, nous évitons principalement les dépendances envers des systèmes externes. Enfin, des bogues dans les systèmes dépendants critiques (tels que les systèmes de fichiers ou les systèmes d'exploitation) ou des malentendus sur leurs conventions peuvent conduire à des erreurs dans FoundationDB. Par exemple, certains bugs sont causés par le fait que la convention réelle du système d'exploitation est plus faible que prévu.

4. Méthodes d'évaluation

Évaluez les performances de FoundationDB à l’aide de charges de travail synthétiques. Plus précisément, il existe quatre types : (1) écriture aveugle, mise à jour du nombre de clés aléatoires configurées ; (2) lecture par intervalles, à partir de la clé aléatoire pour obtenir le nombre de clés consécutives configurées ; (3) lecture par points, obtention de n clés aléatoires. clés ; et (4) écriture de points, obtenir m clés aléatoires et mettre à jour m autres clés aléatoires. Les performances d'écriture et de lecture sont évaluées par écriture aveugle et lecture par intervalles, et la lecture ponctuelle et l'écriture ponctuelle sont utilisées ensemble pour évaluer les performances mixtes de lecture et d'écriture. Assurez-vous que l'ensemble de données ne peut pas être entièrement mis en cache dans la mémoire des StorageServers.

Au débit d'écriture maximum, l'utilisation du processeur du serveur de journaux atteint la saturation. Pour les opérations de lecture et d'écriture, l'augmentation du nombre d'opérations dans une transaction peut améliorer le débit. Cependant, une augmentation supplémentaire du nombre d'opérations n'apporte pas de changements significatifs et l'utilisation du processeur par l'analyseur et le proxy peut atteindre la saturation. Les demandes de validation impliquent plusieurs sauts et une persistance sur trois serveurs de journaux, la latence est donc supérieure à celle des versions en lecture et en lecture. Le traitement par lots permet de maintenir le débit, mais la latence de validation peut augmenter en raison de la saturation.

En raison de la nature orientée client, des temps de reconfiguration courts sont essentiels à la haute disponibilité de ces clusters. Ces temps de récupération courts sont dus au fait qu'ils ne sont pas limités par la taille des données ou du journal des transactions, mais uniquement par la taille des métadonnées du système. Pendant la récupération, les transactions de lecture et d'écriture sont temporairement bloquées et réessayées après un délai d'attente. Toutefois, les lectures client ne sont pas affectées. Les raisons de ces reconfigurations incluent le basculement automatisé en cas de pannes logicielles ou matérielles, les mises à niveau logicielles, les modifications de configuration de base de données et la gestion manuelle des problèmes de production.

5. Fonctionnalités principales de FoundationDB

5.1. Conception architecturale

Le principe de conception diviser pour régner joue un rôle important dans les déploiements cloud, permettant à la base de données d'être à la fois évolutive et performante.

Premièrement, séparer le système de transactions de la couche de stockage permet un déploiement indépendant plus flexible et une expansion des ressources informatiques et de stockage. De plus, l'introduction de serveurs de journaux, similaires aux répliques validées, réduit considérablement le nombre de serveurs de stockage (répliques complètes) requis pour atteindre une haute disponibilité dans certains déploiements de production multirégionaux. Le personnel opérationnel est également libre de déployer différents rôles de FoundationDB sur différents types d'instances de serveur afin d'optimiser les performances et les coûts.

Deuxièmement, cette conception faiblement couplée permet d'étendre les fonctionnalités de la base de données, par exemple en remplaçant le moteur SQLite existant par RocksDB.

Enfin, de nombreuses améliorations de performances peuvent être obtenues en spécialisant les fonctions dans des rôles indépendants, tels que la séparation du distributeur de données et du contrôle de fréquence du flux du séquenceur, l'ajout d'un cache de stockage et la division du proxy en un proxy de version de lecture et un proxy de soumission. Ce modèle de conception atteint l'objectif d'ajouter fréquemment de nouvelles fonctionnalités et d'étendre les capacités.

5.2. Essai de simulation

Les tests de simulation permettent à FoundationDB de maintenir des vitesses de développement élevées avec de petites équipes. Ceci est réalisé en réduisant le délai entre l'introduction d'un bug et sa découverte, et en permettant une reproduction déterministe du problème. Par exemple, l'ajout de journaux supplémentaires n'affecte pas la séquence déterministe des événements, garantissant ainsi une reproduction précise. Cette méthode de débogage est beaucoup plus productive que le débogage normal de l’environnement de production. Dans les rares cas où un bug est découvert pour la première fois dans un environnement réel, le processus de débogage commence généralement par l'amélioration des capacités ou de la précision de la simulation jusqu'à ce que le problème puisse être reproduit en simulation, puis le processus de débogage normal commence. Des tests d'exactitude rigoureux via la simulation rendent FoundationDB extrêmement fiable.

Les tests de simulation continuent de repousser les limites des tests de simulatabilité en éliminant les dépendances et en les implémentant dans Flow. Par exemple, les premières versions de FoundationDB s'appuyaient sur Apache Zookeeper pour la coordination, qui a été remplacée par le nouveau Paxos implémenté dans Flow.

5.3. Récupération rapide

Une récupération rapide contribue non seulement à améliorer la disponibilité, mais simplifie également grandement les mises à niveau logicielles et les modifications de configuration et les rend plus rapides. La méthode traditionnelle de mise à niveau des systèmes distribués consiste à effectuer des mises à niveau progressives afin de pouvoir les annuler en cas de problème. La durée d’une mise à niveau continue peut aller de quelques heures à quelques jours. En revanche, FoundationDB peut effectuer des mises à niveau en redémarrant tous les processus simultanément, généralement en quelques secondes. De plus, ce chemin de mise à niveau simplifie les problèmes de compatibilité des protocoles entre les différentes versions, éliminant ainsi le besoin de garantir la compatibilité du protocole RPC entre les différentes versions du logiciel. De plus, Quick Recovery peut parfois corriger automatiquement les erreurs sous-jacentes, à l’instar de la technologie de résurrection logicielle.

5.4. Fenêtre MVCC de cinq secondes

FoundationDB choisit une fenêtre de contrôle de concurrence multi-version de 5 secondes pour limiter l'utilisation de la mémoire du système de transactions et des serveurs de stockage, car les données multi-versions sont stockées dans la mémoire de l'analyseur et des serveurs de stockage, ce qui limite la taille des transactions. Cette fenêtre de 5 secondes est suffisamment longue pour la plupart des cas d'utilisation du traitement des transactions en ligne. Par conséquent, le dépassement des délais expose souvent des inefficacités dans l’application.

Pour certaines transactions qui peuvent prendre plus de 5 secondes, beaucoup peuvent être divisées en transactions plus petites à gérer. Par exemple, le processus de sauvegarde continue de FoundationDB analyse l'intégralité de l'espace clé et crée des instantanés des plages de clés. En raison de la limite de 5 secondes, le processus de numérisation est divisé en plusieurs petites plages afin que chaque plage puisse être complétée en 5 secondes. En fait, il s’agit d’un modèle courant : une transaction crée plusieurs tâches, et chaque tâche peut être divisée ou exécutée au sein d’une transaction. FoundationDB implémente ce modèle dans une abstraction appelée « TaskBucket », et le système de sauvegarde en dépend fortement.

6. Résumé

FoundationDB est un magasin clé-valeur distribué conçu pour les services cloud OLTP. L'idée principale est de dissocier le traitement des transactions de la journalisation et du stockage. Cette architecture découplée rend possible la séparation et l’expansion horizontale des traitements de lecture et d’écriture. Le système de transaction combine le contrôle de concurrence optimiste (OCC) et le contrôle de concurrence multiversion (MVCC) pour garantir une sérialisation stricte. Le découplage de la journalisation et de l'ordre déterministe des transactions simplifie considérablement le processus de récupération, ce qui se traduit par des temps de récupération exceptionnellement rapides et une disponibilité accrue. Enfin, des simulations déterministes et stochastiques garantissent l'exactitude de la mise en œuvre de la base de données.

[Documents de référence et lectures connexes]

Je suppose que tu aimes

Origine blog.csdn.net/wireless_com/article/details/132797823
conseillé
Classement