Comment gérer les contraintes de clé étrangère lors de la migration du monolithe vers les microservices?

18

Mon équipe migre d'une application ASP.NET monolithique vers .NET Core et Kubernetes. Les changements de code semblent aller aussi bien que prévu, mais là où mon équipe rencontre beaucoup de discorde, c'est autour de la base de données.

Nous avons actuellement une base de données SQL Server assez volumineuse qui héberge toutes les données de l'ensemble de notre entreprise. Je propose que nous divisions la base de données d'une manière similaire à la division du code - les données de catalogue dans une base de données (logique), les données d'inventaire dans une autre, les commandes dans une autre, etc. - et chaque microservice serait le gardien de sa base de données .

L'implication ici est que les clés étrangères qui traversent les frontières des microservices devraient être supprimées et les sprocs et les vues qui traversent les frontières seraient interdits. Tous les modèles de données peuvent résider ou non dans la même base de données physique, mais même s'ils le font, ils ne doivent pas interagir directement les uns avec les autres. Les commandes peuvent toujours référencer les éléments de catalogue par ID, mais l'intégrité des données ne serait pas strictement appliquée au niveau de la base de données et ces données devront être jointes en code plutôt qu'en SQL.

Je vois la perte de ces derniers comme des compromis nécessaires pour passer au microservice et obtenir les avantages d'évolutivité qui vont avec. Tant que nous choisissons judicieusement nos coutures et que nous nous développons autour d'elles, cela devrait être OK. D'autres membres de l'équipe sont convaincus que tout doit rester dans la même base de données monolithique afin que tout puisse être ACID et que l'intégrité référentielle soit préservée partout.

Cela m'amène à ma question. Premièrement, ma position sur les contraintes des clés étrangères et l'adhésion est-elle plausible? Dans l'affirmative, quelqu'un est-il au courant de la lecture crédible que je pourrais offrir à mes collègues? Leur position est presque religieuse et ils ne semblent pas être influencés par quoi que ce soit à moins que Martin Fowler lui-même leur dise qu'ils ont tort.

Raymond Saltrelli
la source
5
L'intégrité référentielle est extrêmement précieuse. L'échelle de la base de données est-elle vraiment le goulot d'étranglement ici? Avez-vous vraiment besoin d'une évolutivité de type microservice? Vous savez mieux que moi si ce changement d'architecture est approprié pour votre organisation, mais veuillez considérer qu'il n'est tout simplement pas adapté à de nombreux cas d'utilisation. Il pourrait y avoir d'autres façons d'évoluer avec des compromis plus attractifs. Par exemple, si les requêtes de base de données par seconde sont trop élevées, la réplication de la base de données suffit peut-être. Et vous pouvez faire évoluer horizontalement les serveurs Web sans avoir à utiliser de microservices.
amon
Bons points. Nous examinons certaines de ces options pour des gains à court terme. Cependant, le passage aux microservices est long. À mon avis, cela nous permettra d'évoluer pendant des années plutôt que des mois.
Raymond Saltrelli,
3
Je suis sûr que vos clients seront ravis que la commande qu'ils ont passée 0,05 ms plus rapidement soit annulée car quelqu'un d'autre a commandé le même produit alors qu'il n'en restait qu'un en stock.
Andy
@amon Faites-en une réponse et je la voterai. C'est une bonne question et les avantages et les inconvénients doivent être équitablement représentés.
mcottle
@mcottle ok, c'est fait!
amon

Réponses:

19

Il n'y a pas de solution claire car cela dépend entièrement de votre contexte - en particulier, selon les dimensions que votre système est censé évoluer et quels sont vos problèmes réels. La base de données est-elle vraiment votre goulot d'étranglement?

Cette réponse (malheureusement assez longue) se lira un peu comme «les microservices sont mauvais, des monolithes à vie!», Mais ce n'est pas mon intention. Mon point est que les microservices et les bases de données distribuées peuvent résoudre divers problèmes, mais non sans avoir certains problèmes qui leur sont propres. Afin de présenter un argument solide pour votre architecture, vous devez montrer que ces problèmes ne s'appliquent pas, peuvent être atténués et que cette architecture est le meilleur choix pour les besoins de votre entreprise.

Les données distribuées sont difficiles.

La même flexibilité qui permet une meilleure mise à l'échelle est le revers des garanties plus faibles. En particulier, les systèmes distribués sont beaucoup plus difficiles à raisonner.

Les mises à jour atomiques, les transactions, la cohérence / l'intégrité référentielle et la durabilité sont extrêmement précieuses et ne doivent pas être abandonnées précipitamment. Il ne sert à rien d'avoir des données si elles sont incomplètes, obsolètes ou carrément erronées. Lorsque ACID est une exigence commerciale mais que vous utilisez une technologie de base de données qui ne peut pas l'offrir immédiatement (par exemple, de nombreuses bases de données NoSQL ou une architecture DB-par-microservice), votre application doit combler le vide et fournir ces garanties.

  • Ce n'est pas impossible à faire, mais difficile à faire. Très délicat. Surtout dans un environnement distribué où il y a plusieurs écrivains dans chaque base de données. Cette difficulté se traduit par un risque élevé de bogues, pouvant inclure des données perdues, des données incohérentes, etc.

    Par exemple, pensez à lire les analyses Jepsen de systèmes de bases de données distribuées bien connus , en commençant peut-être par l' analyse de Cassandra . Je ne comprends pas la moitié de cette analyse, mais le TL; DR est que les systèmes distribués sont si difficiles que même les projets leaders de l'industrie se trompent parfois, d'une manière qui peut sembler évidente avec le recul.

  • Les systèmes distribués impliquent également un effort de développement plus important. Dans une certaine mesure, il y a un compromis direct entre les coûts de développement ou la perte d'argent sur du matériel plus robuste.

Exemple: suspendre des références

En pratique, vous ne devriez pas regarder l'informatique mais les besoins de votre entreprise pour voir si et comment l'ACID peut être assoupli. Par exemple, de nombreuses relations avec des clés étrangères peuvent ne pas être aussi importantes qu'elles le semblent. Considérons une relation produit - catégorie n: m. Dans un SGBDR, nous pourrions utiliser une contrainte de clé étrangère afin que seuls les produits existants et les catégories existantes puissent faire partie de cette relation. Que se passe-t-il si nous introduisons des services de produits et de catégories distincts et qu'un produit ou une catégorie est supprimé?

Dans ce cas, cela pourrait ne pas être un gros problème et nous pouvons écrire notre application afin qu'elle filtre tous les produits ou catégories qui n'existent plus. Mais il y a des compromis!

  • Notez que cela peut nécessiter un niveau d'application JOINsur plusieurs bases de données / microservices, ce qui déplace simplement le traitement du serveur de base de données vers votre application. Cela augmente la charge totale et doit déplacer des données supplémentaires à travers le réseau.

  • Cela peut gâcher la pagination. Par exemple, vous demandez les 25 prochains produits d'une catégorie et filtrez les produits indisponibles de cette réponse. Votre application affiche désormais 23 produits. En théorie, une page avec zéro produit serait également possible!

  • Vous souhaiterez parfois exécuter un script qui nettoie les références pendantes, soit après chaque modification pertinente, soit à intervalles réguliers. Notez que ces scripts sont assez chers car ils doivent demander chaque produit / catégorie à la base de données / microservice de support pour voir s'il existe toujours.

  • Cela devrait être évident, mais pour plus de clarté: ne réutilisez pas les identifiants. Les ID de style à incrémentation automatique peuvent être corrects ou non. Les GUID ou les hachages vous offrent plus de flexibilité, par exemple en pouvant attribuer un ID avant que l'élément ne soit inséré dans une base de données.

Exemple: commandes simultanées

Considérons plutôt une relation produit - commande. Qu'advient-il d'une commande si un produit est supprimé ou modifié? Ok, nous pouvons simplement copier les données produit pertinentes dans l'entrée de commande pour les garder disponibles - échange d'espace disque pour plus de simplicité. Mais que se passe-t-il si le prix du produit change ou si le produit devient indisponible juste avant la commande de ce produit? Dans un système distribué, les effets prennent du temps à se propager et la commande passera probablement par des données obsolètes.

Encore une fois, la façon d'aborder cela dépend des besoins de votre entreprise. Peut-être que la commande obsolète est acceptable, et vous pouvez plus tard annuler la commande si elle ne peut pas être exécutée.

Mais ce n'est peut-être pas une option, par exemple pour les paramètres hautement concurrents. Considérez 3000 personnes se précipitant pour acheter des billets de concert dans les 10 premières secondes, et supposons qu'un changement de disponibilité nécessitera 10 ms pour se propager. Quelle est la probabilité de vendre le dernier billet à plusieurs personnes? Cela dépend de la façon dont ces collisions sont gérées, mais en utilisant une distribution de Poisson avec λ = 3000 / (10s / 10ms) = 3nous avons une P(k > 1) = 1 - P(k = 0) - P(k = 1) = 80%chance de collision par intervalle de 10 ms. Que la vente et l'annulation ultérieure de la majorité de vos commandes soient possibles sans commettre de fraude pourrait conduire à une conversation intéressante avec votre service juridique.

Le pragmatisme signifie choisir les meilleures fonctionnalités.

La bonne nouvelle est que vous n'avez pas besoin de passer à un modèle de base de données distribuée, si ce n'est pas requis autrement. Personne ne révoquera votre adhésion au Microservice Club si vous ne faites pas les microservices «correctement», car il n'y a pas un tel club - et il n'y a pas de véritable moyen de créer des microservices.

Le pragmatisme l'emporte à chaque fois, alors mélangez et associez différentes approches pour résoudre votre problème. Cela pourrait même signifier des microservices avec une base de données centralisée. Vraiment, ne passez pas par la douleur des bases de données distribuées si vous n'êtes pas obligé.

Vous pouvez évoluer sans microservices.

Les microservices présentent deux avantages majeurs:

  • L'avantage organisationnel qu'ils peuvent être développés et déployés indépendamment par des équipes distinctes (ce qui nécessite à son tour que les services offrent une interface stable).
  • L'avantage opérationnel que chaque microservice peut être mis à l'échelle indépendamment .

Si une mise à l'échelle indépendante n'est pas requise, les microservices sont beaucoup moins attrayants.

Un serveur de base de données est déjà une sorte de service que vous pouvez faire évoluer (quelque peu) indépendamment, par exemple en ajoutant des répliques en lecture. Vous mentionnez des procédures stockées. Les réduire pourrait avoir un effet si important que toute autre discussion sur l'évolutivité serait sans objet.

Et il est parfaitement possible d'avoir un monolithe évolutif qui inclut tous les services sous forme de bibliothèques. Vous pouvez ensuite évoluer en lançant plus d'instances du monolithe, ce qui nécessite bien sûr que chaque instance soit sans état.

Cela a tendance à bien fonctionner jusqu'à ce que le monolithe soit trop grand pour être déployé raisonnablement, ou si certains services ont des besoins en ressources spéciaux pour que vous souhaitiez les faire évoluer indépendamment. Les domaines problématiques qui impliquent des ressources supplémentaires peuvent ne pas impliquer un modèle de données distinct.

Avez-vous une solide analyse de rentabilisation?

Vous êtes conscient des besoins commerciaux de votre organisation et pouvez donc créer un argument pour une architecture de base de données par microservice, basée sur une analyse:

  • qu'une certaine échelle est requise, et cette architecture est l'approche la plus rentable pour obtenir cette évolutivité, compte tenu de l'effort de développement accru pour une telle configuration et des solutions alternatives; et
  • que vos exigences commerciales permettent d'assouplir les garanties ACID pertinentes, sans entraîner divers problèmes tels que ceux évoqués ci-dessus.

Inversement, si vous n'êtes pas en mesure de le démontrer, en particulier si la conception actuelle de la base de données est capable de prendre en charge une échelle suffisante dans le futur (comme vos collègues semblent le croire), alors vous avez également votre réponse.

L'évolutivité comporte également un important composant YAGNI. Face à l'incertitude, il s'agit d'une décision commerciale stratégique sur la construction de l'évolutivité maintenant (coûts totaux inférieurs, mais implique des coûts d'opportunité et peut ne pas être nécessaire) par rapport au report de certains travaux sur l'évolutivité (coûts totaux plus élevés si nécessaire, mais vous avez une meilleure idée de l'échelle réelle). Ce n'est pas avant tout une décision technique.

amon
la source
Excellente réponse, merci. Pouvez-vous développer cette déclaration? Voulez-vous dire que la réduction du nombre de procédures pourrait avoir un effet important sur les performances? Vous mentionnez des procédures stockées. Les réduire pourrait avoir un effet si important que toute autre discussion sur l'évolutivité serait sans objet.
Alan
1
@alan Les procédures stockées peuvent être utilisées pour de bon, mais elles posent deux problèmes de performances: (1) Les requêtes plus complexes sont plus difficiles à optimiser pour la base de données. (2) L'utilisation de sprocs signifie faire plus de travail sur le serveur DB. OP souhaite diviser la base de données pour évoluer davantage, mais éviter les sprocs compliqués pourrait déjà fournir cette marge. Bien sûr, les sprocs et les requêtes complexes peuvent également être bons pour les performances, par exemple lorsqu'ils minimisent la quantité de données qui doivent être transférées hors de la base de données pour une réponse à une requête. La division de la base de données aggraverait ce problème lorsque des jointures inter-serveurs sont nécessaires.
amon
0

Je pense que les deux approches sont plausibles. Vous pouvez choisir d'obtenir une évolutivité en sacrifiant les avantages des bases de données ACID et monolithiques, ainsi que de conserver l'architecture actuelle et de sacrifier l'évolutivité et l'agilité d'une architecture plus distribuée. La bonne décision viendra du modèle commercial actuel et de la stratégie buz pour les prochaines années. Du point de vue technologique, il est difficile de le garder monolithique et de passer à une approche plus distribuée. J'analyserais le système et verrais quelles applications / modules / processus métier sont plus critiques pour évoluer et évaluer les risques, les coûts et les avantages pour décider ceux qui devraient attendre ou continuer dans l'architecture monolithique.

brunofl
la source
-1

Votre position est plausible et correcte.

Comment convaincre les toxicomanes teints dans la laine est une autre question. Je dirais que vous avez deux options.

  1. Trouvez un exemple concret où la DB a atteint ses limites. Avez-vous des «tables d'archives» par exemple? Pourquoi ça va? Quel est le nombre maximum de commandes par seconde que vous pouvez prendre? etc. Montrez que la base de données ne répond pas aux exigences et que votre solution les corrige.

  2. Embaucher des entrepreneurs coûteux pour venir vous dire la meilleure solution. Parce que ce sont des dépenses et des blogs, tout le monde les croira

Ewan
la source
1
Je ne suis pas le -1 mais pour que ce soit une bonne réponse, je perdrais l'étourdissement du point 2 et je développerais quand il serait approprié d'avoir besoin de plusieurs bases de données. Je ne pense pas que les tables d'archives soient nécessairement un contre-modèle dans les bases de données qui ne prennent pas en charge le partitionnement. La base de données que j'utilise actuellement contient environ 130 Go de données avec 26 tables> 10 millions de lignes et les performances ne sont pas suffisamment éloignées pour avoir besoin de diviser la base de données; Je suis donc très sceptique et j'aimerais savoir pourquoi c'est une bonne idée, et quand cela doit être fait - cette réponse est la plus proche que j'ai vue jusqu'à présent.
mcottle
bien. Je mentionne les tables d'archives car elles détruisent les contraintes FK. c'est une faille dans l'armure. la division par microservice n'est pas une taille de db, c'est une chose séparable pour vos microservices. Si vous ne pouvez pas en désactiver un et le jeter, ce n'est pas vraiment un microservice. concernant le point 2. l'OP mentionne MF, ils pourraient littéralement l'embaucher / des ateliers de réflexion pour venir leur dire de diviser la base de données
Ewan
"Si vous ne pouvez pas en désactiver un et le jeter, ce n'est pas vraiment un microservice." C'est vrai pour le service lui-même, mais pas nécessairement pour expliquer pourquoi le service a besoin de sa propre base de données. En fin de compte, la base de données est elle-même un service utilisé par le microservice. Le microservice ne sait pas vraiment si les données qu'il utilise se trouvent dans une base de données distincte ou une base de données partagée. Vous pouvez monter ou descendre des copies de ce microservice et rien ne change vraiment.
Chris Pratt
Le meilleur argument pour la base de données par service est les limites de connexion. Il n'est pas rare que le regroupement de connexions soit utilisé, donc chaque microservice nécessite déjà plusieurs connexions à l'instance de base de données, alors vous pouvez avoir plusieurs instances de chacun de ces microservices, chacune avec ses propres pools. Finalement, les choses pourraient arriver à un point critique, où vous avez simplement épuisé la capacité de la base de données à gérer toutes les connexions qu'elle obtient.
Chris Pratt