HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

La prochaine conférence HighLoad++ se tiendra les 6 et 7 avril 2020 à Saint-Pétersbourg.
Détails et billets lien. HighLoad++ Sibérie 2019. Salle "Krasnoïarsk". 25 juin, 12h00. Thèses et présentation.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Il arrive que les exigences pratiques entrent en conflit avec la théorie, où les aspects importants pour un produit commercial ne sont pas pris en compte. Cet exposé présente un processus de sélection et de combinaison de différentes approches pour créer des composants de cohérence causale basés sur des recherches universitaires basées sur les exigences d'un produit commercial. Les auditeurs découvriront les approches théoriques existantes en matière d'horloges logiques, de suivi des dépendances, de sécurité du système, de synchronisation d'horloge et pourquoi MongoDB a opté pour certaines solutions.

Mikhaïl Tyulenev (ci-après dénommé MT) : – Je vais parler de cohérence causale – c'est une fonctionnalité sur laquelle nous avons travaillé dans MongoDB. Je travaille dans un groupe de systèmes distribués, nous l'avons fait il y a environ deux ans.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Dans le processus, j'ai dû me familiariser avec de nombreuses recherches universitaires, car cette fonctionnalité a été assez bien étudiée. Il s'est avéré qu'aucun article ne correspondait à ce qui est requis dans une base de données de production en raison d'exigences très spécifiques qui sont probablement présentes dans toute application de production.

Je parlerai de la façon dont nous, en tant que consommateurs de recherche universitaire, en préparons quelque chose que nous pouvons ensuite présenter à nos utilisateurs sous la forme d'un plat prêt à l'emploi, pratique et sûr à utiliser.

Cohérence causale. Définissons les concepts

Pour commencer, je veux dire en termes généraux ce qu'est la cohérence causale. Il y a deux personnages - Leonard et Penny (série télévisée "The Big Bang Theory") :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Disons que Penny est en Europe et que Leonard veut lui organiser une fête surprise. Et il ne peut penser à rien de mieux que de la retirer de sa liste d'amis, en envoyant à tous ses amis une mise à jour sur le fil : "Rendons Penny heureuse !" (elle est en Europe, pendant qu'elle dort, elle ne voit pas tout cela et ne peut pas le voir, car elle n'y est pas). Finalement, elle supprime ce post, l’efface du Feed et rétablit l’accès pour qu’elle ne s’aperçoive de rien et qu’il n’y ait pas de scandale.
Tout cela est bien beau, mais supposons que le système soit distribué et que les choses se soient un peu mal passées. Il peut, par exemple, arriver que la restriction d'accès de Penny se produise après la parution de ce message, si les événements ne sont pas liés par cause à effet. En fait, il s'agit d'un exemple de cas dans lequel une cohérence causale est requise pour exécuter une fonction commerciale (dans ce cas).

En fait, ce sont des propriétés non triviales de la base de données - très peu de personnes les soutiennent. Passons aux modèles.

Modèles de cohérence

Qu’est-ce qu’exactement un modèle de cohérence dans les bases de données ? Ce sont quelques-unes des garanties qu'offre un système distribué sur les données que le client peut recevoir et dans quel ordre.

En principe, tous les modèles de cohérence se résument à la similitude d'un système distribué avec un système qui s'exécute, par exemple, sur un nœud d'un ordinateur portable. Et voilà à quel point un système qui fonctionne sur des milliers de « nœuds » géo-répartis est similaire à un ordinateur portable, dans lequel toutes ces propriétés sont en principe exécutées automatiquement.

Par conséquent, les modèles de cohérence ne s’appliquent qu’aux systèmes distribués. Tous les systèmes qui existaient auparavant et fonctionnaient à la même échelle verticale n’ont pas rencontré de tels problèmes. Il y avait un cache tampon, et tout y était toujours lu.

Modèle fort

En fait, le tout premier modèle est Strong (ou la ligne de capacité d'élévation, comme on l'appelle souvent). Il s'agit d'un modèle de cohérence qui garantit que chaque changement, une fois confirmé, est visible par tous les utilisateurs du système.

Cela crée un ordre global de tous les événements dans la base de données. Il s’agit d’une propriété de consistance très forte, et elle est généralement très coûteuse. Il est cependant très bien supporté. C'est juste très cher et lent – ​​c'est rarement utilisé. C'est ce qu'on appelle la capacité de montée.

Il existe une autre propriété plus puissante qui est prise en charge dans Spanner, appelée cohérence externe. Nous en reparlerons un peu plus tard.

Causal

Le prochain est Causal, c’est exactement ce dont je parlais. Il existe plusieurs autres sous-niveaux entre Fort et Causal dont je ne parlerai pas, mais ils se résument tous à Causal. C'est un modèle important car c'est le plus solide de tous les modèles, la plus forte consistance en présence d'un réseau ou de cloisons.

Les causalités sont en fait une situation dans laquelle les événements sont liés par une relation de cause à effet. Très souvent, ils sont perçus comme des droits du point de vue du client. Si le client a observé certaines valeurs, il ne peut pas voir les valeurs du passé. Il commence déjà à voir des lectures de préfixes. Tout revient au même.
Les causalités en tant que modèle de cohérence sont un ordre partiel des événements sur le serveur, dans lequel les événements de tous les clients sont observés dans la même séquence. Dans ce cas, Leonard et Penny.

Eventuel

Le troisième modèle est la cohérence éventuelle. C’est ce que prennent en charge absolument tous les systèmes distribués, le modèle minimal qui a du sens. Cela signifie ce qui suit : lorsque nous avons des changements dans les données, à un moment donné, elles deviennent cohérentes.

A un tel moment, elle ne dit rien, sinon elle se transformerait en Cohérence Externe - ce serait une toute autre histoire. Néanmoins, il s’agit d’un modèle très populaire, le plus courant. Par défaut, tous les utilisateurs de systèmes distribués utilisent la cohérence éventuelle.

Je veux donner quelques exemples comparatifs :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Que signifient ces flèches ?

  • Latence. À mesure que la force de cohérence augmente, elle devient plus grande pour des raisons évidentes : vous devez créer plus d'enregistrements, obtenir la confirmation de tous les hôtes et nœuds participant au cluster que les données sont déjà là. En conséquence, la cohérence finale a la réponse la plus rapide, car là, en règle générale, vous pouvez même la mémoriser et cela sera, en principe, suffisant.
  • Disponibilité. Si nous comprenons cela comme la capacité du système à réagir en présence de pannes de réseau, de partitions ou de toute sorte de panne, la tolérance aux pannes augmente à mesure que le modèle de cohérence diminue, puisqu'il nous suffit qu'un hôte vive et en même temps le temps produit des données. La cohérence éventuelle ne garantit rien du tout concernant les données - cela peut être n'importe quoi.
  • Anomalies. Dans le même temps, bien entendu, le nombre d’anomalies augmente. Dans une cohérence forte, ils ne devraient presque pas exister du tout, mais dans une cohérence finale, ils peuvent être n'importe quoi. La question se pose : pourquoi les gens choisissent-ils la cohérence éventuelle si elle contient des anomalies ? La réponse est que les modèles de cohérence éventuelle sont applicables et que des anomalies existent, par exemple, sur une courte période de temps ; il est possible d'utiliser l'assistant pour lire et lire plus ou moins des données cohérentes ; Il est souvent possible d’utiliser des modèles à forte cohérence. En pratique, cela fonctionne, et souvent le nombre d'anomalies est limité dans le temps.

Théorème du CAP

Quand vous voyez les mots cohérence, disponibilité, qu’est-ce qui vous vient à l’esprit ? C'est vrai - le théorème CAP ! Maintenant, je veux dissiper le mythe... Ce n'est pas moi, c'est Martin Kleppmann, qui a écrit un merveilleux article, un merveilleux livre.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Le théorème CAP est un principe formulé dans les années 2000 selon lequel Cohérence, Disponibilité, Partitions : prenez-en deux, et vous ne pouvez pas en choisir trois. C'était un certain principe. Cela a été prouvé sous forme de théorème quelques années plus tard par Gilbert et Lynch. Ensuite, cela a commencé à être utilisé comme un mantra - les systèmes ont commencé à être divisés en CA, CP, AP, etc.

Ce théorème a été effectivement prouvé pour les cas suivants... Premièrement, la disponibilité n'a pas été considérée comme une valeur continue de zéro à des centaines (0 - le système est « mort », 100 - répond rapidement ; nous avons l'habitude de le considérer ainsi) , mais comme une propriété de l'algorithme , qui garantit qu'il renvoie des données pour toutes ses exécutions.

Il n'y a pas du tout un mot sur le temps de réponse ! Il existe un algorithme qui renvoie des données après 100 ans - un algorithme absolument merveilleux, qui fait partie du théorème CAP.
Deuxièmement : le théorème a été prouvé pour les changements de valeurs d'une même clé, malgré le fait que ces changements sont redimensionnables. Cela signifie qu'en réalité, ils ne sont pratiquement pas utilisés, car les modèles sont différents : cohérence éventuelle, cohérence forte (peut-être).

A quoi ça sert tout ça ? De plus, le théorème CAP exactement sous la forme sous laquelle il a été prouvé n'est pratiquement pas applicable et est rarement utilisé. Sous sa forme théorique, cela limite tout en quelque sorte. Il s'avère qu'il existe un certain principe qui est intuitivement correct, mais qui n'a généralement pas été prouvé.

La cohérence causale est le modèle le plus solide

Ce qui se passe maintenant, c'est que vous pouvez obtenir les trois choses : la cohérence et la disponibilité à l'aide des partitions. En particulier, la cohérence causale est le modèle de cohérence le plus fort, qui fonctionne toujours en présence de partitions (ruptures du réseau). C’est pourquoi il présente un si grand intérêt et c’est pourquoi nous l’avons adopté.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Premièrement, cela simplifie le travail des développeurs d’applications. En particulier, la présence d'un grand support de la part du serveur : lorsque tous les enregistrements qui se produisent à l'intérieur d'un client sont garantis d'arriver dans le même ordre sur un autre client. Deuxièmement, il résiste aux cloisons.

Cuisine interne MongoDB

En nous rappelant que c'est le déjeuner, nous nous dirigeons vers la cuisine. Je vais vous parler du modèle du système, à savoir ce qu'est MongoDB pour ceux qui entendent parler d'une telle base de données pour la première fois.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

MongoDB (ci-après dénommé « MongoDB ») est un système distribué qui prend en charge la mise à l'échelle horizontale, c'est-à-dire le partitionnement ; et au sein de chaque partition, il prend également en charge la redondance des données, c'est-à-dire la réplication.

Le partage dans MongoDB (pas une base de données relationnelle) effectue un équilibrage automatique, c'est-à-dire que chaque collection de documents (ou « table » en termes de données relationnelles) est divisée en morceaux et le serveur les déplace automatiquement entre les fragments.

Le Query Router, qui distribue les requêtes pour un client, est un client à travers lequel il fonctionne. Il sait déjà où et quelles données se trouvent et dirige toutes les requêtes vers le bon fragment.

Autre point important : MongoDB est un maître unique. Il existe un primaire : il peut accepter des enregistrements prenant en charge les clés qu'il contient. Vous ne pouvez pas effectuer d'écriture multi-maître.

Nous avons réalisé la version 4.2 - de nouvelles choses intéressantes y sont apparues. En particulier, ils ont inséré Lucene - recherche - à savoir l'exécutable Java directement dans Mongo, et là, il est devenu possible d'effectuer des recherches via Lucene, comme dans Elastica.

Et ils ont créé un nouveau produit - Charts, il est également disponible sur Atlas (le propre Cloud de Mongo). Ils ont un niveau gratuit – vous pouvez jouer avec. J'ai beaucoup aimé Charts - visualisation de données, très intuitive.

Ingrédients Cohérence causale

J'ai compté environ 230 articles publiés sur ce sujet - par Leslie Lampert. Maintenant, de mémoire, je vais vous transmettre quelques parties de ces documents.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Tout a commencé avec un article de Leslie Lampert, rédigé dans les années 1970. Comme vous pouvez le constater, certaines recherches sur ce sujet sont toujours en cours. Aujourd'hui, la cohérence causale suscite un intérêt dans le cadre du développement de systèmes distribués.

Restrictions

Quelles sont les restrictions ? C’est en fait l’un des points principaux, car les restrictions qu’impose un système de production sont très différentes des restrictions qui existent dans les articles académiques. Ils sont souvent assez artificiels.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

  • Premièrement, « MongoDB » est un maître unique, comme je l'ai déjà dit (cela simplifie grandement).
  • Nous pensons que le système devrait prendre en charge environ 10 XNUMX fragments. Nous ne pouvons prendre aucune décision architecturale qui limiterait explicitement cette valeur.
  • Nous avons un cloud, mais nous supposons qu'une personne devrait toujours avoir la possibilité de télécharger un fichier binaire, de l'exécuter sur son ordinateur portable et que tout fonctionne à merveille.
  • Nous supposons quelque chose que Research suppose rarement : les clients externes peuvent faire ce qu’ils veulent. MongoDB est open source. En conséquence, les clients peuvent être si intelligents et en colère qu'ils peuvent vouloir tout casser. Nous pensons que les Feilors byzantins pourraient être originaires.
  • Pour les clients externes situés en dehors du périmètre, il existe une limitation importante : si cette fonctionnalité est désactivée, aucune dégradation des performances ne doit être observée.
  • Un autre point est globalement anti-académique : la compatibilité des versions précédentes et des futures. Les anciens pilotes doivent prendre en charge les nouvelles mises à jour et la base de données doit prendre en charge les anciens pilotes.

En général, tout cela impose des restrictions.

Composants de cohérence causale

Je vais maintenant parler de certains des composants. Si nous considérons la cohérence causale en général, nous pouvons sélectionner des blocs. Nous avons choisi parmi les travaux qui appartiennent à un certain bloc : le suivi des dépendances, le choix des horloges, la manière dont ces horloges peuvent être synchronisées les unes avec les autres et la manière dont nous assurons la sécurité - voici un aperçu de ce dont je vais parler :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Suivi complet des dépendances

Pourquoi est-ce nécessaire ? Ainsi, lorsque les données sont répliquées, chaque enregistrement, chaque modification de données contient des informations sur les modifications dont elle dépend. Le tout premier et naïf changement se produit lorsque chaque message contenant un enregistrement contient des informations sur les messages précédents :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Dans cet exemple, le nombre entre accolades correspond au nombre d'enregistrements. Parfois ces enregistrements avec des valeurs sont même transférés dans leur intégralité, parfois certaines versions sont transférées. L'essentiel est que chaque changement contient des informations sur le précédent (portant évidemment tout cela en lui).

Pourquoi avons-nous décidé de ne pas utiliser cette approche (full tracking) ? Évidemment, parce que cette approche n'est pas pratique : toute modification apportée à un réseau social dépend de toutes les modifications précédentes apportées à ce réseau social, en transférant, par exemple, Facebook ou VKontakte à chaque mise à jour. Néanmoins, il existe de nombreuses recherches sur le suivi complet des dépendances – ce sont des réseaux pré-sociaux ; dans certaines situations, cela fonctionne vraiment.

Suivi explicite des dépendances

Le suivant est plus limité. Le transfert d'informations est également considéré ici, mais uniquement celui qui en est clairement dépendant. Ce qui dépend de quoi, en règle générale, est déterminé par la demande. Lorsque les données sont répliquées, la requête renvoie uniquement les réponses lorsque les dépendances précédentes ont été satisfaites, c'est-à-dire affichées. C’est l’essence même du fonctionnement de la cohérence causale.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Elle voit que l'enregistrement 5 dépend des enregistrements 1, 2, 3, 4 - en conséquence, elle attend avant que le client ait accès aux modifications apportées par la décision d'accès de Penny, alors que toutes les modifications précédentes ont déjà transité par la base de données.

Cela ne nous convient pas non plus, car il y a encore trop d’informations, et cela va ralentir les choses. Il existe une autre approche...

Horloge Lamport

Ils sont très vieux. Lamport Clock signifie que ces dépendances sont intégrées dans une fonction scalaire, appelée Lamport Clock.

Une fonction scalaire est un nombre abstrait. On l’appelle souvent le temps logique. A chaque événement, ce compteur augmente. Le compteur, actuellement connu du processus, envoie chaque message. Il est clair que les processus peuvent être désynchronisés, ils peuvent avoir des moments complètement différents. Néanmoins, le système équilibre d’une manière ou d’une autre l’horloge avec de tels messages. Que se passe-t-il dans ce cas?

J'ai divisé ce gros fragment en deux pour que ce soit clair : les amis peuvent vivre dans un nœud, qui contient un morceau de la collection, et Feed peut vivre dans un autre nœud, qui contient un morceau de cette collection. Est-il clair comment ils peuvent sortir des sentiers battus ? Le premier flux dira : "Répliqué", puis "Amis". Si le système ne fournit pas une sorte de garantie que le flux ne sera pas affiché tant que les dépendances Friends de la collection Friends ne seront pas également livrées, nous nous retrouverons alors exactement dans la situation que j'ai mentionnée.

Vous voyez comment le temps du compteur sur Feed augmente logiquement :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Ainsi, la propriété principale de cette cohérence Lamport Clock et Causal (expliquée via Lamport Clock) est la suivante : si nous avons les événements A et B, et que l'événement B dépend de l'événement A*, alors il s'ensuit que le LogicalTime de l'événement A est inférieur à LogicalTime de l’événement B.

* Parfois, ils disent aussi que A s'est produit avant B, c'est-à-dire que A s'est produit avant B - c'est une certaine relation qui commande partiellement l'ensemble des événements qui se sont produits en général.

Le contraire est incorrect. C’est en fait l’un des principaux inconvénients de Lamport Clock : l’ordre partiel. Il existe un concept d'événements simultanés, c'est-à-dire des événements dans lesquels ni (A ne s'est produit avant B) ni (A ne s'est produit avant B). Un exemple serait l’ajout parallèle par Leonard de quelqu’un d’autre comme ami (pas même Leonard, mais Sheldon, par exemple).
C'est la propriété qui est souvent utilisée lorsqu'on travaille avec les horloges Lamport : ils regardent spécifiquement la fonction et en concluent que ces événements sont peut-être dépendants. Parce qu'une première solution est vraie : si LogicalTime A est inférieur à LogicalTime B, alors B ne peut pas arriver avant A ; et si plus, alors peut-être.

Horloge vectorielle

Le développement logique de l’horloge Lamport est l’horloge vectorielle. Ils diffèrent en ce sens que chaque nœud présent ici contient sa propre horloge distincte et qu'ils sont transmis sous forme de vecteur.
Dans ce cas, vous voyez que le zéroième indice du vecteur est responsable du flux et que le premier indice du vecteur est celui des amis (chacun de ces nœuds). Et maintenant ils vont augmenter : l'indice zéro de « Feed » augmente lors de l'écriture – 1, 2, 3 :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Pourquoi l'horloge vectorielle est-elle meilleure ? Parce qu'ils vous permettent de déterminer quels événements sont simultanés et quand ils se produisent sur différents nœuds. Ceci est très important pour un système de partitionnement comme MongoDB. Cependant, nous n'avons pas choisi cela, même si c'est une chose merveilleuse, qui fonctionne très bien et qui nous conviendrait probablement...

Si nous avons 10 10 fragments, nous ne pouvons pas transférer XNUMX XNUMX composants, même si nous les compressons ou proposons autre chose - la charge utile sera toujours plusieurs fois inférieure au volume de l'ensemble de ce vecteur. C’est pourquoi, en serrant le cœur et les dents, nous avons abandonné cette approche et sommes passés à une autre.

Clé TrueTime. Horloge atomique

J'ai dit qu'il y aurait une histoire sur Spanner. C'est un truc sympa, tout droit sorti du XNUMXème siècle : horloges atomiques, synchronisation GPS.

Quelle est l'idée ? "Spanner" est un système de Google qui est même récemment devenu accessible aux gens (ils y ont ajouté SQL). Chaque transaction y a un horodatage. Puisque le temps est synchronisé*, chaque événement peut se voir attribuer une heure spécifique - les horloges atomiques ont un temps d'attente, après lequel une heure différente est garantie de « se produire ».

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Ainsi, en écrivant simplement dans la base de données et en attendant un certain temps, la sérialisabilité de l'événement est automatiquement garantie. Ils ont le modèle de cohérence le plus solide qu'on puisse imaginer en principe : c'est la cohérence externe.

* C'est le principal problème des horloges Lampart : elles ne sont jamais synchrones sur les systèmes distribués. Ils peuvent diverger ; même avec NTP, ils ne fonctionnent toujours pas très bien. "Spanner" a une horloge atomique et la synchronisation, semble-t-il, se fait en microsecondes.

Pourquoi n'avons-nous pas choisi ? Nous ne supposons pas que nos utilisateurs disposent d’une horloge atomique intégrée. Lorsqu'elles apparaîtront, intégrées à chaque ordinateur portable, il y aura une sorte de synchronisation GPS super cool - alors oui... Mais pour l'instant, le meilleur qui soit possible est Amazon, les stations de base - pour les fanatiques... Nous avons donc utilisé d'autres montres .

Horloge hybride

C'est en fait ce qui se passe dans MongoDB pour garantir la cohérence causale. Comment sont-ils hybrides ? Hybride est une valeur scalaire, mais elle comporte deux composantes :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

  • La première est l’époque Unix (combien de secondes se sont écoulées depuis le « début du monde informatique »).
  • Le second est un incrément, également un entier non signé de 32 bits.

C'est tout, en fait. Il existe cette approche : la partie responsable du temps est tout le temps synchronisée avec l'horloge ; chaque fois qu'une mise à jour se produit, cette partie est synchronisée avec l'horloge et il s'avère que l'heure est toujours plus ou moins correcte, et l'incrément permet de distinguer les événements qui se sont produits au même moment.

Pourquoi est-ce important pour MongoDB ? Parce que cela vous permet de créer une sorte de restauration de sauvegarde à un moment donné, c'est-à-dire que l'événement est indexé dans le temps. Ceci est important lorsque certains événements sont nécessaires ; Pour une base de données, les événements sont des changements dans la base de données qui se sont produits à certains intervalles de temps.

Je ne vous dirai la raison la plus importante que pour vous (s'il vous plaît, ne le dites à personne) ! Nous avons fait cela parce que c'est à cela que ressemblent les données organisées et indexées dans MongoDB OpLog. OpLog est une structure de données qui contient absolument toutes les modifications apportées à la base de données : elles vont d'abord à OpLog, puis elles sont appliquées au stockage lui-même dans le cas où il s'agit d'une date ou d'un fragment répliqué.

C'était la raison principale. Néanmoins, le développement d'une base de données comporte également des exigences pratiques, ce qui signifie qu'elle doit être simple : peu de code, le moins d'éléments défectueux possible qui doivent être réécrits et testés. Le fait que nos oplogs soient indexés par des horloges hybrides a beaucoup aidé et nous a permis de faire le bon choix. Cela a vraiment porté ses fruits et a fonctionné comme par magie sur le tout premier prototype. C'était très cool !

Synchronisation de l'horloge

Il existe plusieurs méthodes de synchronisation décrites dans la littérature scientifique. Je parle de synchronisation lorsque nous avons deux fragments différents. S'il existe un jeu de réplicas, aucune synchronisation n'est nécessaire : il s'agit d'un « maître unique » ; nous avons un OpLog, dans lequel tombent toutes les modifications - dans ce cas, tout est déjà ordonné séquentiellement dans « Oplog » lui-même. Mais si nous avons deux fragments différents, la synchronisation temporelle est ici importante. C’est là que l’horloge vectorielle a le plus aidé ! Mais nous ne les avons pas.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Le second convient - c'est "Heartbeats". Il est possible d'échanger certains signaux qui se produisent à chaque unité de temps. Mais les Heartbeats sont trop lents, nous ne pouvons pas fournir de latence à notre client.

Le temps réel est bien entendu une chose merveilleuse. Mais, encore une fois, c'est probablement l'avenir... Même si cela peut déjà être fait dans Atlas, il existe déjà des synchroniseurs horaires rapides « Amazon ». Mais il ne sera pas accessible à tout le monde.

Les commérages, c'est quand tous les messages incluent du temps. C'est à peu près ce que nous utilisons. Chaque message entre nœuds, un pilote, un routeur de nœud de données, absolument tout pour MongoDB est une sorte d'élément, un composant de base de données qui contient une horloge qui s'exécute. Ils ont partout le sens du temps hybride, il se transmet. 64 bits ? Cela permet, c'est possible.

Comment tout cela fonctionne-t-il ensemble ?

Ici, je regarde une réplique pour rendre les choses un peu plus faciles. Il y a le Primaire et le Secondaire. Le secondaire effectue la réplication et n'est pas toujours complètement synchronisé avec le primaire.

Une insertion se produit dans le « Primery » avec une certaine valeur temporelle. Cet insert augmente le compte interne de 11, si c'est le maximum. Ou il vérifiera les valeurs de l'horloge et se synchronisera avec l'horloge si les valeurs de l'horloge sont supérieures. Cela vous permet de vous organiser par temps.

Après avoir réalisé l’enregistrement, un moment important survient. L'horloge est dans "MongoDB" et n'est incrémentée qu'en cas d'écriture dans "Oplog". C'est l'événement qui change l'état du système. Dans absolument tous les articles classiques, un événement est considéré comme le moment où un message atteint un nœud : le message est arrivé, ce qui signifie que le système a changé d'état.

Cela est dû au fait qu'au cours des recherches, il n'est pas tout à fait clair comment ce message sera interprété. Nous savons avec certitude que s'il n'est pas reflété dans l'« Oplog », alors il ne sera en aucun cas interprété, et un changement dans l'état du système n'est qu'une entrée dans l'« Oplog ». Cela simplifie tout pour nous : le modèle le simplifie et nous permet de l'organiser au sein d'un seul jeu de répliques, et bien d'autres choses utiles.

La valeur qui est déjà écrite dans « Oplog » est renvoyée - nous savons que « Oplog » contient déjà cette valeur et son heure est 12. Maintenant, disons, la lecture commence à partir d'un autre nœud (secondaire), et elle transmet afterClusterTime dans le message. Il dit : « J'ai besoin de tout ce qui s'est passé au moins après midi ou pendant midi » (voir photo ci-dessus).

C'est ce qu'on appelle le Causal A Consistant (CAT). Il existe en théorie un tel concept selon lequel il s'agit d'une tranche de temps cohérente en soi. Dans ce cas, on peut dire que c'est l'état du système qui a été observé au temps 12.

Maintenant, il n'y a encore rien ici, car cela simule en quelque sorte la situation dans laquelle vous avez besoin du secondaire pour répliquer les données du primaire. Il attend... Et maintenant les données sont arrivées - il renvoie ces valeurs.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

C'est à peu près ainsi que tout fonctionne. Presque.

Que signifie « presque » ? Supposons qu'il y ait une personne qui ait lu et compris comment tout cela fonctionne. J'ai réalisé que chaque fois que ClusterTime se produit, il met à jour l'horloge logique interne, puis l'entrée suivante augmente de un. Cette fonction prend 20 lignes. Disons que cette personne transmet le plus grand nombre de 64 bits, moins un.

Pourquoi « moins un » ? Parce que l'horloge interne sera remplacée par cette valeur (évidemment, c'est la plus grande possible et supérieure à l'heure actuelle), alors une entrée aura lieu dans "Oplog", et l'horloge sera incrémentée d'une autre unité - et il y aura déjà être une valeur maximale (il y a simplement toutes les unités, il n'y a nulle part où aller), unsaints ints).

Il est clair qu’après cela, le système devient absolument inaccessible à quoi que ce soit. Il ne peut être que déchargé et nettoyé, ce qui représente beaucoup de travail manuel. Disponibilité totale :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

De plus, si cela est répliqué ailleurs, le cluster tout entier s’effondrera tout simplement. Une situation absolument inacceptable que chacun peut organiser très rapidement et facilement ! Nous avons donc considéré ce moment comme l’un des plus importants. Comment l’empêcher ?

Notre façon est de signer clusterTime

C'est ainsi qu'il est transmis dans le message (avant le texte en bleu). Mais nous avons aussi commencé à générer une signature (texte bleu) :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

La signature est générée par une clé stockée dans la base de données, à l'intérieur d'un périmètre sécurisé ; lui-même est généré et mis à jour (les utilisateurs n’y voient rien). Un hachage est généré et chaque message est signé lors de sa création et validé lors de sa réception.
La question se pose probablement dans l’esprit des gens : « Dans quelle mesure cela ralentit-il les choses ? Je vous ai dit que cela devrait fonctionner rapidement, surtout en l'absence de cette fonctionnalité.

Que signifie utiliser la cohérence causale dans ce cas ? Il s'agit d'afficher le paramètre afterClusterTime. Sans cela, il transmettra simplement des valeurs de toute façon. Les commérages, à partir de la version 3.6, fonctionnent toujours.

Si nous laissons la génération constante de signatures, cela ralentira le système même en l'absence d'une fonctionnalité qui ne répond pas à nos approches et exigences. Alors qu'est ce qu'on a fait?

Fais le rapidement!

C’est une chose assez simple, mais l’astuce est intéressante – je la partagerai, peut-être que quelqu’un sera intéressé.
Nous avons un hachage qui stocke les données signées. Toutes les données passent par le cache. Le cache ne signe pas l'heure précise, mais la Range. Lorsqu'une valeur arrive, nous générons un Range, masquons les 16 derniers bits et nous signons cette valeur :

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

En recevant une telle signature, nous accélérons le système (relativement) 65 10 fois. Cela fonctionne très bien : lorsque nous avons effectué des expériences, le temps a en fait diminué de XNUMX XNUMX fois lorsque nous avions une mise à jour séquentielle. Il est clair que lorsqu’ils sont en désaccord, cela ne marche pas. Mais dans la plupart des cas pratiques, cela fonctionne. La combinaison de la signature Range avec la signature a résolu le problème de sécurité.

Qu'avons-nous appris ?

Les leçons que nous en avons tirées :

  • Nous devons lire du matériel, des histoires, des articles, car nous avons beaucoup de choses intéressantes. Lorsque nous travaillons sur une fonctionnalité (surtout maintenant, lorsque nous effectuons des transactions, etc.), nous devons lire et comprendre. Cela prend du temps, mais c'est en fait très utile car cela montre clairement où nous en sommes. Il semble que nous n’ayons rien trouvé de nouveau – nous avons juste pris les ingrédients.

    En général, il existe une certaine différence de réflexion lorsqu'il y a une conférence académique (Sigmon, par exemple) : tout le monde se concentre sur de nouvelles idées. Quoi de neuf dans notre algorithme ? Il n’y a rien de particulièrement nouveau ici. La nouveauté réside plutôt dans la manière dont nous avons fusionné les approches existantes. La première chose à faire est donc de lire les classiques, à commencer par Lampart.

  • En production, les exigences sont complètement différentes. Je suis sûr que beaucoup d'entre vous ne sont pas confrontés à des bases de données « sphériques » dans un vide abstrait, mais à des éléments normaux et réels qui présentent des problèmes de disponibilité, de latence et de tolérance aux pannes.
  • La dernière chose est que nous avons dû considérer différentes idées et combiner plusieurs articles complètement différents en une seule approche. L'idée de signer, par exemple, est généralement venue d'un article qui considérait le protocole Paxos, qui pour les Failors non byzantins est à l'intérieur du protocole d'autorisation, pour les Byzantins - en dehors du protocole d'autorisation... En général, c'est exactement ce que nous fini par faire.

    Il n’y a absolument rien de nouveau ici ! Mais dès qu'on a mélangé le tout... C'est comme dire que la recette de la salade Olivier est une absurdité, car les œufs, la mayonnaise et les concombres ont déjà été inventés... C'est à peu près la même histoire.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Je vais terminer avec ça. Merci!

des questions

Question du public (ci-après dénommée B) : – Merci, Mikhail, pour le rapport ! Le sujet du temps est intéressant. Vous utilisez les commérages. Ils ont dit que chacun avait son heure, que chacun connaissait son heure locale. Si je comprends bien, nous avons un pilote - il peut y avoir de nombreux clients avec des pilotes, des planificateurs de requêtes aussi, des fragments aussi... Et à quoi se résume le système si nous avons soudainement un écart : quelqu'un décide que c'est pour un une minute d'avance, quelqu'un une minute de retard ? Où allons-nous finir ?

MT : – Excellente question en effet ! Je voulais juste parler des fragments. Si je comprends bien la question, nous avons la situation suivante : il y a le fragment 1 et le fragment 2, la lecture se fait à partir de ces deux fragments - ils ont un écart, ils n'interagissent pas entre eux, car l'heure qu'ils connaissent est différente, surtout la fois où ils existent dans les oplogs.
Disons que le fragment 1 a créé un million d'enregistrements, que le fragment 2 n'a rien fait du tout et que la demande est arrivée à deux fragments. Et le premier a un afterClusterTime de plus d'un million. Dans une telle situation, comme je l'ai expliqué, le fragment 2 ne répondra jamais du tout.

В: – Je voulais savoir comment ils se synchronisent et choisissent une heure logique ?

MT : - Très facile à synchroniser. Shard, quand afterClusterTime vient à lui et qu'il ne trouve pas le temps dans le « Oplog », n'initie aucune approbation. C'est-à-dire qu'il élève son temps avec ses mains à cette valeur. Cela signifie qu'il n'y a aucun événement correspondant à cette demande. Il crée cet événement artificiellement et devient ainsi Causal Cohérent.

В: – Et si après cela il lui arrivait d'autres événements qui ont été perdus quelque part dans le réseau ?

MT : – Shard est conçu de telle manière qu’ils ne reviendront plus, puisqu’il s’agit d’un seul maître. S'il s'est déjà inscrit, ils ne viendront pas, mais viendront plus tard. Il ne peut pas arriver que quelque chose reste bloqué quelque part, qu’il n’écrive pas et que ces événements arrivent – ​​et que la cohérence causale soit rompue. S'il n'écrit pas, ils devraient tous venir ensuite (il les attendra).

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

В: – J’ai plusieurs questions concernant les files d’attente. La cohérence causale suppose qu'il existe une file d'attente spécifique d'actions qui doivent être effectuées. Que se passe-t-il si l'un de nos colis disparaît ? Voici le 10, le 11... le 12 a disparu, et tout le monde attend qu'il se réalise. Et soudain notre voiture est morte, nous ne pouvons rien faire. Y a-t-il une longueur maximale de file d'attente qui s'accumule avant d'être exécutée ? Quel échec fatal se produit lorsqu’un État est perdu ? De plus, si nous écrivons qu'il existe un état antérieur, alors devrions-nous en quelque sorte repartir de là ? Mais ils ne l’ont pas repoussé !

MT : – C’est aussi une excellente question ! Qu'est-ce que nous faisons? MongoDB a le concept d'écritures de quorum, de lectures de quorum. Dans quels cas un message peut-il être perdu ? Lorsqu'une écriture n'atteint pas le quorum ou lorsqu'une lecture n'atteint pas le quorum (une sorte de déchet peut également rester).
Concernant la cohérence causale, un vaste test expérimental a été réalisé, dont le résultat a été que dans le cas où les écritures et les lectures ne sont pas quorum, des violations de la cohérence causale se produisent. Exactement ce que tu dis !

Notre conseil : utilisez au moins la lecture du quorum lorsque vous utilisez la cohérence causale. Dans ce cas, rien ne sera perdu, même si l'enregistrement de quorum est perdu... Il s'agit d'une situation orthogonale : si l'utilisateur ne souhaite pas que des données soient perdues, il doit utiliser un enregistrement de quorum. La cohérence causale ne garantit pas la durabilité. La durabilité est garantie par la réplication et les machines associées à la réplication.

В: – Lorsque nous créons une instance qui effectue le sharding pour nous (respectivement non pas maître, mais esclave), elle s'appuie sur l'heure Unix de sa propre machine ou sur l'heure du « maître » ; Est-ce qu'il se synchronise pour la première fois ou périodiquement ?

MT : – Je vais clarifier maintenant. Shard (c'est-à-dire partition horizontale) – il y a toujours un primaire à cet endroit. Et un fragment peut avoir un « maître » et il peut y avoir des répliques. Mais le fragment prend toujours en charge l'enregistrement, car il doit prendre en charge un domaine (le fragment a Primary).

В: – Donc tout dépend uniquement du « maître » ? L'heure maître est-elle toujours utilisée ?

MT : - Oui. Vous pouvez dire au sens figuré : le temps presse lorsqu'une entrée dans le « maître », dans l'« Oplog » se produit.

В: – Nous avons un client qui se connecte et qui n’a pas besoin de connaître l’heure ?

MT : – Vous n’avez pas besoin de savoir quoi que ce soit ! Si nous parlons de la façon dont cela fonctionne sur le client : lorsque le client souhaite utiliser la cohérence causale, il doit ouvrir une session. Désormais, tout est là : les transactions dans la session, et la récupération des droits... Une session est l'ordre des événements logiques se produisant avec le client.

S'il ouvre cette session et y dit qu'il souhaite une cohérence causale (si la session prend en charge la cohérence causale par défaut), tout fonctionne automatiquement. Le conducteur mémorise ce temps et l'augmente lorsqu'il reçoit un nouveau message. Il se souvient de la réponse que la précédente a renvoyée du serveur qui a renvoyé les données. La requête suivante contiendra afterCluster("temps supérieur à cela").

Le client n’a absolument rien besoin de savoir ! Cela lui est complètement opaque. Si les gens utilisent ces fonctionnalités, que peuvent-ils faire ? Premièrement, vous pouvez lire les secondaires en toute sécurité : vous pouvez écrire dans le primaire et lire à partir des secondaires géographiquement répliqués et être sûr que cela fonctionne. Dans le même temps, les sessions enregistrées sur le primaire peuvent même être transférées vers le secondaire, c'est-à-dire vous pouvez utiliser non pas une session, mais plusieurs.

В: – Une nouvelle couche de science informatique – les types de données CRDT (Conflict-free Replicated Data Types) – est fortement liée au thème de la cohérence éventuelle. Avez-vous envisagé d'intégrer ce type de données dans la base de données et que pouvez-vous en dire ?

MT : - Bonne question! CRDT est logique pour les conflits d'écriture : dans MongoDB, maître unique.

В: – J’ai une question des développeurs. Dans le monde réel, existe-t-il de telles situations jésuitiques où l'échec byzantin se produit et où des personnes maléfiques à l'intérieur du périmètre protégé commencent à pénétrer dans le protocole, à envoyer des colis artisanaux d'une manière spéciale ?

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

MT : – Les méchants à l’intérieur du périmètre sont comme un cheval de Troie ! Les personnes malveillantes à l’intérieur du périmètre peuvent faire beaucoup de mauvaises choses.

В: – Il est clair que laisser, grosso modo, un trou dans le serveur à travers lequel vous pouvez mettre un zoo d'éléphants et effondrer tout le cluster pour toujours... La récupération manuelle prendra du temps... Ceci, pour le moins, est faux. Par contre, c'est intéressant : dans la vraie vie, dans la pratique, il y a des situations où des attaques internes naturellement similaires se produisent ?

MT : – Comme je rencontre rarement des failles de sécurité dans la vraie vie, je ne peux pas dire si elles se produisent. Mais si nous parlons de philosophie de développement, nous pensons ainsi : nous avons un périmètre qui assure les gars qui assurent la sécurité - c'est un château, un mur ; et à l'intérieur du périmètre, vous pouvez faire ce que vous voulez. Il est clair qu'il existe des utilisateurs ayant la possibilité de visualiser uniquement, et d'autres ayant la possibilité d'effacer le répertoire.

Selon les droits, les dommages que les utilisateurs peuvent causer peuvent être une souris ou un éléphant. Il est clair qu’un utilisateur disposant de tous les droits peut tout faire. Un utilisateur disposant de droits limités peut causer beaucoup moins de dommages. En particulier, il ne peut pas briser le système.

В: – Dans le périmètre protégé, quelqu'un a essayé de créer des protocoles inattendus pour le serveur afin de détruire complètement le serveur, et si vous avez de la chance, le cluster tout entier... Est-ce que ça arrive parfois à ce « bien » ?

MT : "Je n'ai jamais entendu parler de telles choses." Le fait que vous puissiez faire planter un serveur de cette manière n’est un secret pour personne. Échouer à l'intérieur, étant issu du protocole, étant un utilisateur autorisé qui peut écrire quelque chose comme ça dans le message... En fait, c'est impossible, car cela sera quand même vérifié. Il est possible de désactiver cette authentification pour les utilisateurs qui ne le souhaitent pas - alors c'est leur problème ; ils ont, grosso modo, détruit les murs eux-mêmes et vous pouvez y pousser un éléphant qui piétinera... Mais en général, vous pouvez vous déguiser en réparateur, venez le retirer !

В: – Merci pour le rapport. Sergueï (Yandex). Il existe une constante dans Mong qui limite le nombre de membres votants dans l'ensemble de répliques, et cette constante est 7 (sept). Pourquoi est-ce une constante ? Pourquoi n'est-ce pas une sorte de paramètre ?

MT : – Nous avons des ensembles de répliques avec 40 nœuds. Il y a toujours une majorité. Je ne sais pas quelle version...

В: – Dans Replica Set, vous pouvez exécuter des membres non votants, mais il y a un maximum de 7 membres votants. Comment pouvons-nous survivre à l'arrêt dans ce cas si notre Replica Set est réparti sur 3 centres de données ? Un centre de données peut facilement s'éteindre et une autre machine peut tomber.

MT : – Cela dépasse déjà un peu le cadre du rapport. C'est une question générale. Peut-être que je pourrai vous en parler plus tard.

HighLoad++, Mikhail Tyulenev (MongoDB) : Cohérence causale : de la théorie à la pratique

Quelques publicités 🙂

Merci de rester avec nous. Vous aimez nos articles ? Vous voulez voir du contenu plus intéressant ? Soutenez-nous en passant une commande ou en recommandant à vos amis, cloud VPS pour les développeurs à partir de 4.99 $, un analogue unique des serveurs d'entrée de gamme, que nous avons inventé pour vous : Toute la vérité sur le VPS (KVM) E5-2697 v3 (6 Cores) 10Go DDR4 480Go SSD 1Gbps à partir de 19$ ou comment partager un serveur ? (disponible avec RAID1 et RAID10, jusqu'à 24 cœurs et jusqu'à 40 Go de DDR4).

Dell R730xd 2 fois moins cher dans le centre de données Equinix Tier IV à Amsterdam ? Ici seulement 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV à partir de 199$ aux Pays-Bas! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - à partir de 99$ ! En savoir plus Comment construire une infrastructure corp. classe avec l'utilisation de serveurs Dell R730xd E5-2650 v4 qui valent 9000 XNUMX euros pour un sou ?

Source: habr.com

Ajouter un commentaire