Pourquoi une réplication semi-synchrone peut-elle être nécessaire ?

Bonjour à tous. Ici Vladislav Rodin. Actuellement, j'enseigne sur la plateforme OTUS des cours sur l'architecture des logiciels et l'architecture des logiciels soumis à une charge élevée. À l'approche du lancement d'un nouveau groupe de cours, « Architecte de haute charge » j'ai décidé d'écrire un petit article que je souhaite partager avec vous.

Pourquoi une réplication semi-synchrone peut-elle être nécessaire ?

Introduction

En raison du fait qu'un HDD ne peut réaliser qu'environ 400 à 700 opérations par seconde (ce qui est incomparable avec les rps typiques d'un système à forte charge), une base de données classique devient le goulet d'étranglement de l'architecture. Par conséquent, il est essentiel de porter une attention particulière aux patterns de mise à l'échelle de ce type de stockage.

Actuellement, il existe 2 modèles de mise à l'échelle des bases de données : la réplication et le sharding. Le sharding permet de mettre à l'échelle l'opération d'écriture et, par conséquent, de réduire le rps d'écriture sur un seul serveur de votre cluster. La réplication permet de faire la même chose, mais pour les opérations de lecture. C'est à ce modèle que cet article est consacré.

Réplique

Si l'on considère la réplication à un niveau très général, c'est une chose simple : vous aviez un serveur, où étaient stockées vos données, puis ce serveur ne pouvait plus gérer la charge de lecture de ces données. Vous ajoutez quelques serveurs supplémentaires, synchronisez les données sur tous les serveurs, et l'utilisateur peut lire à partir de n'importe quel serveur de votre cluster.

Malgré sa simplicité apparente, il existe plusieurs variantes de classification des différentes réalisations de ce schéma :

  • Par rôle dans le cluster (maître-maître ou maître-esclave)
  • Par objets envoyés (basé sur les lignes, basé sur les instructions ou mixte)
  • Par mécanisme de synchronisation des nœuds

Aujourd'hui, nous allons nous concentrer précisément sur le troisième point.

Comment se déroule le commit d'une transaction

Ce sujet n'est pas directement lié à la réplication et pourrait faire l'objet d'un article à part entière, cependant, étant donné que sans comprendre le mécanisme de commit d'une transaction, la lecture suivante serait inutile, permettez-moi de rappeler les points les plus fondamentaux. Le commit d'une transaction se déroule en 3 étapes :

  1. Enregistrement de la transaction dans le journal de la base de données.
  2. Application de la transaction dans le moteur de la base de données.
  3. Retour d'une confirmation au client sa confirmant l'application réussie de la transaction.

Dans différentes bases de données, ce schéma peut présenter des nuances : par exemple, dans le moteur InnoDB de MySQL, il y a deux journaux : un pour la réplication (binary log) et un autre pour maintenir l'ACID (undo/red log), tandis que dans PostgreSQL, il n'y a qu'un seul journal qui effectue les deux fonctions (write ahead log = WAL). Cependant, ci-dessus est présentée la conception générale qui permet de ne pas tenir compte de ces nuances.

Réplication synchrone (sync)

Ajoutons à l'algorithme de validation de transaction la logique de réplication des changements reçus :

  1. Enregistrement de la transaction dans le journal de la base de données.
  2. Application de la transaction dans le moteur de la base de données.
  3. Envoi des données à toutes les répliques.
  4. Obtention de la confirmation de toutes les répliques sur l'exécution de la transaction.
  5. Retour d'une confirmation au client sa confirmant l'application réussie de la transaction.

Avec cette approche, nous rencontrons plusieurs inconvénients :

  • le client attend l'application des changements sur toutes les répliques.
  • avec l'augmentation du nombre de nœuds dans le cluster, nous diminuons la probabilité de succès de l'opération d'écriture.

Si le premier point est plus ou moins clair, les raisons du deuxième point méritent une explication. Si, en réplication synchrone, nous ne recevons pas de réponse d'au moins un nœud, nous annulons la transaction. Ainsi, en augmentant le nombre de nœuds dans le cluster, vous augmentez la probabilité que l'opération d'écriture échoue.

Pouvons-nous attendre la confirmation seulement d'une partie des nœuds, par exemple, de 51 % (quorum) ? Oui, nous le pouvons, mais dans la version classique, une confirmation de tous les nœuds est requise, car c'est ainsi que nous pouvons garantir la pleine consistance des données dans le cluster, ce qui est sans aucun doute un avantage de ce type de réplication.

Réplication asynchrone (async)

Modifions l'algorithme précédent. Nous enverrons les données aux répliques « à un moment donné », et « à un moment donné », les changements seront appliqués sur les répliques :

  1. Enregistrement de la transaction dans le journal de la base de données.
  2. Application de la transaction dans le moteur de la base de données.
  3. Retour d'une confirmation au client sa confirmant l'application réussie de la transaction.
  4. Envoi des données aux répliques et application des changements par celles-ci.

Cette approche conduit à un cluster qui fonctionne rapidement, car nous ne tenons pas le client en attente pendant que les données arrivent aux répliques et sont validées.

Mais la condition d'envoi des données aux répliques « à un moment donné » peut entraîner une perte de transaction, en particulier la perte d'une transaction confirmée à l'utilisateur, car si les données n'ont pas eu le temps d'être répliquées, la confirmation de l'opération réussie est envoyée au client, et si le disque dur du nœud qui a reçu les changements tombe en panne, nous perdons la transaction, ce qui peut avoir des conséquences très désagréables.

Réplication semi-synchrone (semisync)

Enfin, nous en sommes arrivés à la réplication semi-synchrone. Ce type de réplication n'est pas très connu et assez rare, mais il présente un intérêt considérable car il peut combiner les avantages de la réplication synchrone et asynchrone.

Essayons de combiner les deux approches précédentes. Ne gardons pas le client trop longtemps, mais exigeons que les données soient répliquées :

  1. Enregistrement de la transaction dans le journal de la base de données.
  2. Application de la transaction dans le moteur de la base de données.
  3. Envoi des données vers les répliques.
  4. Obtention d'une confirmation de la réplique concernant la réception des modifications (elles seront appliquées « un peu plus tard »).
  5. Retour d'une confirmation au client sa confirmant l'application réussie de la transaction.

Notez que, dans ce schéma, la perte de transaction ne se produit que si à la fois le nœud recevant les modifications et le nœud réplique tombent en panne. La probabilité d'une telle défaillance est considérée comme faible, et ces risques sont acceptés.

Cependant, avec cette approche, il existe un risque de lectures fantômes. Imaginons le scénario suivant : à l'étape 4, nous n'avons reçu de confirmation d'aucune réplique. Nous devons annuler cette transaction, sans faire de retour de confirmation au client. Étant donné que les données ont été appliquées à l'étape 2, entre la fin de l'étape 2 et l'annulation de la transaction, un intervalle de temps survient pendant lequel des transactions parallèles peuvent voir ces modifications qui ne devraient pas exister dans la base.

Réplication semi-synchrone sans perte

Si l'on réfléchit un peu, on peut simplement changer l'ordre des étapes de l'algorithme pour corriger le problème des lectures fantômes dans ce scénario :

  1. Enregistrement de la transaction dans le journal de la base de données.
  2. Envoi des données à la réplique.
  3. Obtention d'une confirmation de la réplique concernant la réception des modifications (elles seront appliquées « un peu plus tard »).
  4. Application de la transaction dans le moteur de la base de données.
  5. Retour d'une confirmation au client sa confirmant l'application réussie de la transaction.

Nous ne validons désormais les modifications que si elles ont été répliquées.

Sortie

Comme toujours, il n'existe pas de solutions idéales, mais plutôt un ensemble de solutions, chacune ayant ses avantages et ses inconvénients, adaptées à différents types de problèmes. Cela est tout à fait vrai pour le choix du mécanisme de synchronisation des données d'une base de données répliquée. L'ensemble des avantages de la réplication semi-synchrone est suffisamment solide et intéressant pour être reconnu comme méritant attention, malgré sa rare utilisation.

C'est tout. À bientôt sur cours!

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster