Systèmes de bases de données distribuées 101
Ou encore, bases de données distribuées: que signifie FK « échelle Web »?
Les systèmes de base de données distribués sont des créatures complexes et se déclinent en plusieurs versions. Si j'approfondis mes études universitaires à ce sujet, dont je ne me souviens plus très bien, j'essaierai d'expliquer certains des problèmes d'ingénierie essentiels à la création d'un système de base de données réparti.
Tout d'abord, une terminologie
Propriétés ACID (Atomicité, Cohérence, Isolation et Durabilité): Il s'agit des invariants clés à appliquer pour qu'une transaction soit mise en œuvre de manière fiable sans provoquer d'effets secondaires indésirables.
Atomicity nécessite que la transaction soit terminée ou annulée complètement. Les transactions partiellement terminées ne doivent jamais être visibles et le système doit être construit de manière à empêcher cela.
La cohérence requiert qu'une transaction ne viole jamais aucun invariant (tel que l'intégrité référentielle déclarative) garanti par le schéma de base de données. Par exemple, si une clé étrangère existe, il devrait être impossible d'insérer un enregistrement enfant dans le respect d'un parent inexistant.
L’isolement exige que les transactions ne se gênent pas. Le système doit garantir les mêmes résultats si les transactions sont exécutées en parallèle ou de manière séquentielle. Dans la pratique, la plupart des produits de SGBDR permettent d’alterner entre les modes qui isolent performance.
La durabilité nécessite qu’une fois validée, la transaction reste dans un stockage persistant d’une manière qui résiste aux pannes matérielles ou logicielles.
Je vais expliquer ci-dessous certains des obstacles techniques que ces exigences présentent sur les systèmes distribués.
Architecture de disque partagé: architecture dans laquelle tous les nœuds de traitement d'un cluster ont accès à l'ensemble du stockage. Cela peut constituer un goulot d'étranglement central pour l'accès aux données. Oracle RAC ou Exadata est un exemple de système à disque partagé .
Architecture Shared Nothing: architecture dans laquelle les nœuds de traitement d'un cluster disposent d'un stockage local invisible pour les autres nœuds du cluster. Teradata et Netezza sont des exemples de systèmes sans partage.
Architecture de mémoire partagée: Architecture dans laquelle plusieurs processeurs (ou nœuds) peuvent accéder à un pool de mémoire partagé. La plupart des serveurs modernes sont du type à mémoire partagée. La mémoire partagée facilite certaines opérations telles que les caches ou les primitives de synchronisation atomique, qui sont beaucoup plus difficiles à effectuer sur les systèmes distribués.
Synchronisation: terme générique décrivant diverses méthodes permettant de garantir un accès cohérent à une ressource partagée par plusieurs processus ou threads. Cela est beaucoup plus difficile à faire sur les systèmes distribués que sur les systèmes à mémoire partagée, bien que certaines architectures de réseau (par exemple, BYNET de Teradata) aient des primitives de synchronisation dans le protocole réseau. La synchronisation peut également entraîner une surcharge importante.
Semi-jointure: Primitive utilisée pour joindre des données contenues dans deux nœuds différents d'un système distribué. Essentiellement, il contient suffisamment d'informations sur les lignes à joindre qui doivent être regroupées et transmises d'un noeud à l'autre afin de résoudre la jointure. Sur une requête volumineuse, cela pourrait impliquer un trafic réseau important.
Cohérence éventuelle: terme utilisé pour décrire la sémantique de transaction qui substitue une mise à jour immédiate (cohérence des lectures) sur tous les nœuds d'un système distribué pour des performances (et donc un débit de transaction supérieur) en écriture. La cohérence éventuelle est un effet secondaire de l'utilisation de la réplication de quorum en tant qu'optimisation des performances pour accélérer les validations de transaction dans des bases de données distribuées où plusieurs copies de données sont conservées sur des nœuds distincts.
Algorithme de Lamport: algorithme pour la mise en œuvre d'une exclusion mutuelle (synchronisation) sur des systèmes sans mémoire partagée. Normalement, l’exclusion mutuelle dans un système nécessite une instruction atomique de lecture-comparaison-écriture ou un ordre similaire d’un type qui ne s’applique normalement que sur un système à mémoire partagée. Il existe d'autres algorithmes de synchronisation distribués, mais celui de Lamport était l'un des premiers et est le plus connu. Comme la plupart des mécanismes de synchronisation distribués, l'algorithme de Lamport est fortement dépendant du minutage et de la synchronisation d'horloge précis entre les nœuds de la grappe.
Two Phase Commit (2PC): ensemble de protocoles garantissant que les mises à jour de bases de données impliquant plusieurs systèmes physiques sont validées ou restaurées de manière cohérente. Que 2PC soit utilisé dans un système ou sur plusieurs systèmes via un gestionnaire de transactions, il entraîne une surcharge considérable.
Dans un protocole de validation en deux phases, le gestionnaire de transactions demande aux nœuds participants de conserver la transaction de manière à garantir leur validation, puis de signaler cet état. Lorsque tous les nœuds ont renvoyé un statut "heureux", il leur signale leur validation. La transaction est toujours considérée comme ouverte jusqu'à ce que tous les nœuds envoient une réponse indiquant que la validation est terminée. Si un nœud tombe en panne avant de signaler que la validation est terminée, le gestionnaire de transactions l'interrogera à nouveau lorsqu'il reviendra jusqu'à ce qu'il obtienne une réponse positive indiquant que la transaction a été validée.
MVCC (Multi-Version Concurrency Control): gérez les conflits en écrivant de nouvelles versions des données dans un emplacement différent et en permettant aux autres transactions de voir l'ancienne version des données jusqu'à ce que la nouvelle version soit validée. Cela réduit les conflits de base de données aux dépens d'un trafic d'écriture supplémentaire pour écrire la nouvelle version, puis marquer l'ancienne version comme obsolète.
Algorithme d'élection: Les systèmes distribués impliquant plusieurs nœuds sont intrinsèquement moins fiables qu'un système unique car il existe plus de modes de défaillance. Dans de nombreux cas, les systèmes en cluster ont besoin d’un mécanisme leur permettant de faire face à la défaillance d’un nœud. Les algorithmes d'élection sont une classe d'algorithmes utilisés pour sélectionner un chef de file afin de coordonner un calcul distribué dans des situations où le nœud de «chef» n'est pas déterminé ou fiable à 100%.
Partitionnement horizontal: une table peut être divisée en plusieurs nœuds ou volumes de stockage par sa clé. Cela permet de fractionner un grand volume de données en fragments plus petits et de les répartir sur des nœuds de stockage.
Mise en fragmentation: un ensemble de données peut être partitionné horizontalement sur plusieurs nœuds physiques dans une architecture sans partage. Lorsque ce partitionnement n’est pas transparent (c’est-à-dire que le client doit connaître le schéma de partition et déterminer quel nœud interroger explicitement), on parle de partage. Certains systèmes (Teradata, par exemple) divisent les données en nœuds, mais leur emplacement est transparent pour le client. le terme n'est normalement pas utilisé avec ce type de système.
Cohérence cohérente: algorithme utilisé pour attribuer des données à des partitions en fonction de la clé. Il se caractérise par une distribution uniforme des touches de hachage et par la capacité d'élargir ou de réduire de manière élastique le nombre de compartiments efficacement. Ces attributs le rendent utile pour le partitionnement de données ou la charge sur un cluster de nœuds dont la taille peut changer de manière dynamique, des nœuds étant ajoutés ou supprimés du cluster (peut-être en raison d'une défaillance).
Réplication multimaître: technique permettant de répliquer des écritures sur plusieurs nœuds d'un cluster sur les autres nœuds. Cette technique facilite la mise à l'échelle en permettant à certaines tables d'être partitionnées ou fragmentées sur des serveurs et à d'autres d'être synchronisées sur le cluster. Les écritures doivent être répliquées sur tous les nœuds et non sur un quorum. Par conséquent, les validations de transaction sont plus coûteuses sur une architecture répliquée à plusieurs maîtres que sur un système répliqué à quorum.
Commutateur non bloquant: commutateur réseau utilisant le parallélisme matériel interne pour obtenir un débit proportionnel au nombre de ports sans goulots d'étranglement internes. Une implémentation naïve peut utiliser un mécanisme de barre transversale, mais celle-ci a une complexité de O (N ^ 2) pour N ports, la limitant à des commutateurs plus petits. Les grands commutateurs peuvent utiliser davantage une topologie interne complexe appelée commutateur d’extension minimal non bloquant pour obtenir une mise à l’échelle du débit linéaire sans nécessiter de matériel O (N ^ 2).
Créer un SGBD distribué - à quel point peut-il être difficile?
Plusieurs difficultés techniques rendent cette tâche assez difficile à mettre en pratique. Outre la complexité supplémentaire liée à la création d'un système distribué, l'architecte d'un SGBD distribué doit résoudre certains problèmes d'ingénierie délicats.
Atomicité sur les systèmes distribués: si les données mises à jour par une transaction sont réparties sur plusieurs nœuds, la validation / l'annulation des nœuds doit être coordonnée. Cela ajoute une surcharge significative sur les systèmes sans partage. Sur les systèmes à disques partagés, le problème est moins grave car tout le stockage peut être vu par tous les nœuds, ce qui permet à un seul nœud de coordonner la validation.
Cohérence sur les systèmes distribués: Pour prendre l'exemple de clé étrangère cité ci-dessus, le système doit pouvoir évaluer un état cohérent. Par exemple, si le parent et l'enfant d'une relation de clé étrangère peuvent résider sur des nœuds différents, un mécanisme de verrouillage distribué est nécessaire pour garantir que les informations obsolètes ne sont pas utilisées pour valider la transaction. Si cela n'est pas appliqué, vous pourriez avoir (par exemple) une condition de concurrence critique selon laquelle le parent est supprimé après vérification de sa présence avant d'autoriser l'insertion de l'enfant.
L'application tardive des contraintes (c'est-à-dire l'attente de la validation de DRI) exige que le verrou soit maintenu pendant toute la durée de la transaction. Ce type de verrouillage distribué entraîne des frais généraux considérables.
Si plusieurs copies de données sont conservées (ceci peut être nécessaire sur des systèmes sans partage, pour éviter un trafic réseau inutile provenant de semi-jointures), toutes les copies des données doivent être mises à jour.
Isolation sur des systèmes répartis: lorsque les données affectées par une transaction résident sur plusieurs nœuds système, les verrous et la version (si MVCC est utilisé) doivent être synchronisés entre les nœuds. Pour garantir la sérialisation des opérations, en particulier sur les architectures sans partage, dans lesquelles des copies redondantes de données peuvent être stockées, un mécanisme de synchronisation distribuée, tel que l'algorithme de Lamport, entraîne également une surcharge importante du trafic réseau.
Durabilité sur les systèmes distribués: sur un système à disques partagés, le problème de durabilité est essentiellement identique à celui d'un système à mémoire partagée, à l'exception du fait que des protocoles de synchronisation distribués sont toujours nécessaires sur tous les nœuds. Le SGBD doit écrire dans le journal et écrire les données de manière cohérente. Sur un système sans partage, il peut y avoir plusieurs copies des données ou des parties des données stockées sur différents nœuds. Un protocole de validation en deux phases est nécessaire pour garantir que la validation se déroule correctement entre les nœuds. Cela entraîne également des frais généraux importants.
Sur un système sans partage, la perte d'un nœud peut signifier que les données ne sont pas disponibles pour le système. Pour atténuer ces données, vous pouvez répliquer sur plusieurs nœuds. La cohérence dans cette situation signifie que les données doivent être répliquées sur tous les nœuds où elles résident normalement. Cela peut entraîner des frais généraux substantiels sur les écritures.
Une optimisation courante des systèmes NoSQL consiste à utiliser la réplication de quorum et la cohérence éventuelle pour permettre la réplication des données paresseuse tout en garantissant un certain niveau de résilience des données en écrivant dans un quorum avant de signaler la transaction comme étant validée. Les données sont ensuite répliquées paresseusement sur les autres nœuds où se trouvent des copies des données.
Notez que la «cohérence éventuelle» est un compromis important en termes de cohérence qui peut ne pas être acceptable si les données doivent être consultées de manière cohérente dès que la transaction est validée. Par exemple, sur une application financière, un solde mis à jour doit être disponible immédiatement.
Systèmes à disques partagés
Un système à disque partagé est un système où tous les nœuds ont accès à tout le stockage. Ainsi, le calcul est indépendant de l'emplacement. De nombreuses plates-formes de SGBD peuvent également fonctionner dans ce mode - Oracle RAC est un exemple d'une telle architecture.
Les systèmes de disques partagés peuvent évoluer considérablement car ils peuvent prendre en charge une relation M: M entre les noeuds de stockage et les noeuds de traitement. Un SAN peut avoir plusieurs contrôleurs et plusieurs serveurs peuvent exécuter la base de données. Ces architectures ont un commutateur comme goulet d’étranglement central, mais les commutateurs à barre transversale permettent à ce commutateur d’avoir beaucoup de bande passante. Certains traitements peuvent être déchargés sur les nœuds de stockage (comme dans le cas d'Exadata d'Oracle), ce qui peut réduire le trafic sur la bande passante de stockage.
Bien que le commutateur soit théoriquement un goulet d'étranglement, la bande passante disponible signifie que les architectures à disques partagés évolueront de manière assez efficace pour les grands volumes de transactions. La plupart des architectures de SGBD classiques adoptent cette approche car elle offre une évolutivité «satisfaisante» et une grande fiabilité. Avec une architecture de stockage redondante telle que Fibre Channel, il n'y a pas de point de défaillance unique, car il existe au moins deux chemins d'accès entre un nœud de traitement et un nœud de stockage.
Systèmes Shared-Nothing
Les systèmes sans partage sont des systèmes où au moins une partie des données est conservée localement sur un nœud et n'est pas directement visible pour les autres nœuds. Cela supprime le goulot d'étranglement d'un commutateur central, permettant à la base de données de s'adapter (au moins en théorie) au nombre de nœuds. Le partitionnement horizontal permet aux données d'être réparties sur plusieurs nœuds. cela peut être transparent pour le client ou non (voir Sharding ci-dessus).
Étant donné que les données sont distribuées de manière inhérente, une requête peut nécessiter des données provenant de plusieurs nœuds. Si une jointure a besoin de données provenant de différents nœuds, une opération de semi-jointure est utilisée pour transférer suffisamment de données pour prendre en charge la jointure d'un nœud à un autre. Cela peut entraîner un trafic réseau important. L'optimisation de la distribution des données peut donc considérablement améliorer les performances des requêtes.
Souvent, les données sont répliquées sur des nœuds d'un système partagé-rien pour réduire le besoin de semi-jointures. Cela fonctionne assez bien sur les appliances d'entrepôt de données car les dimensions sont généralement inférieures de plusieurs ordres de grandeur aux tables de faits et peuvent être facilement répliquées sur des nœuds. Ils sont également généralement chargés par lots, de sorte que la charge de réplication est moins problématique que dans une application transactionnelle.
Le parallélisme inhérent à une architecture sans partage ne les rend bien adaptés au type de requêtes d'analyse de table / agrégats caractéristiques d'un entrepôt de données. Ce type d'opération peut évoluer presque linéairement avec le nombre de nœuds de traitement. Les jointures importantes sur les nœuds ont tendance à générer davantage de temps système car les opérations de semi-jointure peuvent générer beaucoup de trafic réseau.
Le déplacement de gros volumes de données est moins utile pour les applications de traitement de transactions, où la surcharge de plusieurs mises à jour rend ce type d'architecture moins attrayant qu'un disque partagé. Ainsi, ce type d'architecture a tendance à ne pas être utilisé largement en dehors des applications d'entrepôt de données.
Sharding, réplication de quorum et cohérence éventuelle
La réplication de quorum est une fonction dans laquelle un SGBD réplique les données pour une haute disponibilité. Cela est utile pour les systèmes conçus pour fonctionner sur du matériel standard moins cher, dépourvu de fonctionnalités de haute disponibilité intégrées, comme un SAN. Dans ce type de système, les données sont répliquées sur plusieurs nœuds de stockage pour des performances de lecture et un stockage redondant afin de rendre le système résilient aux pannes matérielles d'un nœud.
Cependant, la réplication des écritures sur tous les nœuds est O (M x N) pour M nœuds et N écritures. Cela rend les écritures coûteuses si elles doivent être répliquées sur tous les nœuds avant qu'une transaction ne soit autorisée à être validée. La réplication de quorum est un compromis qui permet de répliquer immédiatement les écritures sur un sous-ensemble de nœuds, puis d'écrire paresseusement sur les autres nœuds à l'aide d'une tâche en arrière-plan. Les écritures peuvent être validées plus rapidement, tout en offrant un certain degré de redondance en veillant à ce qu'elles soient répliquées vers un sous-ensemble minimal (quorum) de nœuds avant que la transaction ne soit signalée comme validée au client.
Cela signifie que les lectures de nœuds en dehors du quorum peuvent afficher des versions obsolètes des données jusqu'à ce que le processus d'arrière-plan ait fini d'écrire des données sur le reste des nœuds. La sémantique est appelée "Cohérence éventuelle" et peut être ou ne pas être acceptable selon les exigences de votre application, mais signifie que les validations de transaction sont plus proches de O (1) que de O (n) dans l'utilisation des ressources.
La fragmentation nécessite que le client connaisse le partitionnement des données au sein des bases de données, en utilisant souvent un type d'algorithme appelé "cohérence du hachage". Dans une base de données partagée, le client hache la clé pour déterminer le serveur du cluster sur lequel émettre la requête. Comme les demandes sont réparties sur les nœuds du cluster, il n’ya pas de goulot d’étranglement avec un seul nœud de coordinateur de requête.
Ces techniques permettent à une base de données d'évoluer à une vitesse presque linéaire en ajoutant des nœuds au cluster. Théoriquement, la réplication de quorum n'est nécessaire que si le support de stockage sous-jacent doit être considéré comme non fiable. Cela est utile si des serveurs de base doivent être utilisés, mais a moins de valeur si le mécanisme de stockage sous-jacent possède son propre schéma de haute disponibilité (par exemple, un réseau de stockage avec des contrôleurs en miroir et une connectivité à plusieurs chemins avec les hôtes).
Par exemple, la table BigTable de Google n'implémente pas la réplication de quorum en tant que telle, bien qu'elle repose sur GFS, un système de fichiers en cluster qui utilise la réplication de quorum. BigTable (ou tout autre système sans partage) pourrait utiliser un système de stockage fiable avec plusieurs contrôleurs et partitionner les données entre les contrôleurs. L'accès parallèle serait alors obtenu par le partitionnement des données.
Retour aux plateformes de SGBDR
Il n'y a aucune raison inhérente pour que ces techniques ne puissent pas être utilisées avec un SGBDR. Cependant, la gestion des verrous et des versions serait assez complexe sur un tel système et tout marché pour un tel système sera probablement très spécialisé. Aucune des plates-formes de SGBDR classiques n'utilise la réplication de quorum et je ne suis pas au courant de l'existence d'un produit de SGBDR (du moins pas avec un abonnement significatif).
Les systèmes à disque partagé et sans système partagé ne peuvent pas évoluer vers des charges de travail très volumineuses. Par exemple, Oracle RAC peut prendre en charge 63 nœuds de traitement (qui peuvent être de grandes machines SMP) et un nombre arbitraire de contrôleurs de stockage sur le réseau SAN. Un IBM Sysplex (un cluster d'ordinateurs centraux zSeries) peut prendre en charge plusieurs ordinateurs centraux (chacun disposant d'une puissance de traitement et d'une bande passante d'E / S considérables) et de plusieurs contrôleurs SAN. Ces architectures peuvent prendre en charge de très gros volumes de transactions avec la sémantique ACID, bien qu'elles supposent un stockage fiable. Teradata, Netezza et d'autres éditeurs développent des plates-formes d'analyse hautes performances basées sur des conceptions sans partage qui s'adaptent à des volumes de données extrêmement importants.
Jusqu'ici, le marché des plates-formes SGBDR SGBD entièrement ACID ultra-volumineuses, mais à très faible volume, est dominé par MySQL, qui prend en charge la réplication partagée et multi-maîtres. MySQL n'utilise pas la réplication de quorum pour optimiser le débit d'écriture. Par conséquent, les validations de transaction sont plus coûteuses que sur un système NoSQL. La fragmentation autorise des débits de lecture très élevés (Facebook utilise par exemple de manière intensive MySQL), de sorte que ce type d'architecture s'adapte bien aux charges de travail nécessitant une lecture importante.
Un débat intéressant
BigTable est une architecture sans partage (essentiellement une paire clé-valeur distribuée), comme l'a souligné Michael Hausenblas ci-dessous . Mon évaluation initiale incluait le moteur MapReduce, qui ne fait pas partie de BigTable, mais devrait normalement être utilisé conjointement avec celui-ci dans ses implémentations les plus courantes (par exemple, Hadoop / HBase et le framework MapReduce de Google).
En comparant cette architecture à Teradata, qui possède une affinité physique entre stockage et traitement (les nœuds ont un stockage local plutôt qu'un réseau SAN partagé), vous pourriez affirmer que BigTable / MapReduce est une architecture de disque partagée reposant sur le système de stockage parallèle visible de manière globale.
Le débit de traitement d'un système de style MapReduce tel que Hadoop est limité par la bande passante d'un commutateur réseau non bloquant. 1 Les commutateurs non bloquants peuvent toutefois gérer des agrégats à large bande passante en raison du parallélisme inhérent à la conception. Ils constituent donc rarement une contrainte pratique significative en termes de performances. Cela signifie qu'une architecture de disque partagé (peut-être mieux appelé système de stockage partagé) peut s'adapter à des charges de travail importantes, même si le commutateur réseau constitue théoriquement un goulot d'étranglement central.
Le point de départ était de noter que, même si ce goulet d’étranglement central existe dans les systèmes à disques partagés, un sous-système de stockage partitionné avec plusieurs noeuds de stockage (serveurs de tablette BigTable ou contrôleurs SAN, par exemple) peut toujours évoluer à grande échelle. Une architecture de commutateur non bloquante peut (en théorie) gérer autant de connexions actuelles que de ports.
1 Bien entendu, le traitement et le débit d'E / S disponibles constituent également une limite de performances, mais le commutateur de réseau est un point central par lequel tout le trafic passe.