Wie gehe ich bei der Migration von Monolithen zu Microservices mit Fremdschlüsseleinschränkungen um?


18

Mein Team migriert von einer monolithischen ASP.NET-Anwendung auf .NET Core und Kubernetes. Die Codeänderungen scheinen so gut zu verlaufen, wie erwartet, aber wo mein Team auf viel Zwietracht stößt, ist die Datenbank.

Derzeit verfügen wir über eine relativ große SQL Server-Datenbank, in der alle Daten für unser gesamtes Unternehmen gespeichert sind. Ich schlage vor, dass wir die Datenbank auf ähnliche Weise wie den Code aufteilen - Katalogdaten in eine (logische) Datenbank, Inventardaten in eine andere, Bestellungen in eine andere usw. - und jeder Mikrodienst wäre der Gatekeeper für seine Datenbank .

Dies impliziert, dass Fremdschlüssel, die Mikrodienstgrenzen überschreiten, entfernt werden müssten und Sprocs und Ansichten, die über Grenzen hinweg reichen, verboten wären. Alle Datenmodelle können sich in derselben physischen Datenbank befinden oder nicht, aber selbst wenn dies der Fall ist, sollten sie nicht direkt miteinander interagieren. Bestellungen verweisen möglicherweise immer noch auf Katalogelemente nach ID, aber die Datenintegrität wird auf Datenbankebene nicht strikt durchgesetzt, und die Daten müssen in Code und nicht in SQL verknüpft werden.

Ich betrachte den Verlust dieser Funktionen als notwendigen Kompromiss, wenn es darum geht, auf Microservice umzusteigen und die damit verbundenen Vorteile für die Skalierbarkeit zu erzielen. Solange wir unsere Nähte mit Bedacht auswählen und um sie herum entwickeln, sollte es in Ordnung sein. Andere Teammitglieder sind der festen Überzeugung, dass sich alles in derselben monolithischen Datenbank befinden muss, damit alles SÄURE sein und die referenzielle Integrität überall erhalten bleibt.

Das bringt mich zu meiner Frage. Erstens, ist meine Haltung zu Fremdschlüsseleinschränkungen und zum Beitritt plausibel? Wenn ja, ist jemandem glaubwürdiges Lesematerial bekannt, das ich meinen Kollegen anbieten könnte? Ihre Position ist fast religiös und sie scheinen nicht von irgendetwas anderem beeinflusst zu werden, als dass Martin Fowler ihnen sagt, dass sie falsch liegen.


5
Referentielle Integrität ist äußerst wertvoll. Ist der Umfang der Datenbank hier wirklich der Engpass? Benötigen Sie wirklich eine Skalierbarkeit im Mikroservice-Stil? Sie wissen besser als ich, ob diese Änderung in der Architektur für Ihr Unternehmen geeignet ist. Beachten Sie jedoch, dass sie für viele Anwendungsfälle einfach nicht geeignet ist. Es gibt möglicherweise andere Möglichkeiten, um mit attraktiveren Kompromissen zu skalieren. Wenn beispielsweise die Datenbankabfragen pro Sekunde zu hoch sind, ist möglicherweise nur eine Datenbankreplikation erforderlich. Sie können Webserver horizontal skalieren, ohne Microservices verwenden zu müssen.
Amon

Gute Argumente. Wir prüfen einige dieser Optionen für kurzfristige Gewinne. Der Wechsel zu Microservices ist jedoch das lange Spiel. Meiner Meinung nach können wir damit Jahre statt Monate skalieren.
Raymond Saltrelli

3
Ich bin mir sicher, dass Ihre Kunden begeistert sein werden, dass die Bestellung, die sie 0.05ms schneller aufgegeben haben, storniert wird, weil jemand anderes dasselbe Produkt bestellt hat, als es nur noch ein Produkt auf Lager gab.
Andy

@amon Mach das als Antwort und ich stimme dem zu. Dies ist eine gute Frage, und die Vor- und Nachteile müssen fair vertreten sein.
Mcottle

@mcottle ok, fertig!
amon

Antworten:


19

Es gibt keine eindeutige Lösung, da dies ausschließlich von Ihrem Kontext abhängt - insbesondere davon, in welchen Dimensionen Ihr System skalieren soll und welche tatsächlichen Probleme Sie haben. Ist die Datenbank wirklich Ihr Engpass?

Diese (leider etwas langwierige) Antwort lautet ein bisschen wie "Microservices sind schlecht, Monolithen fürs Leben!", Aber das ist nicht meine Absicht. Mein Punkt ist, dass Microservices und verteilte Datenbanken verschiedene Probleme lösen können, aber nicht ohne eigene Probleme. Um ein starkes Argument für Ihre Architektur zu liefern, müssen Sie nachweisen, dass diese Probleme nicht zutreffen, behoben werden können und dass diese Architektur die beste Wahl für Ihre Geschäftsanforderungen ist.

Verteilte Daten sind schwierig.

Die gleiche Flexibilität, die eine bessere Skalierung ermöglicht, ist die Kehrseite schwächerer Garantien. Insbesondere sind verteilte Systeme viel schwieriger zu beurteilen.

Atomare Aktualisierungen, Transaktionen, Konsistenz / referenzielle Integrität und Beständigkeit sind äußerst wertvoll und sollten nicht vorschnell aufgegeben werden. Es macht wenig Sinn, Daten zu haben, die unvollständig, veraltet oder völlig falsch sind. Wenn Sie ACID als Geschäftsanforderung haben, jedoch eine Datenbanktechnologie verwenden, die dies nicht ohne weiteres bietet (z. B. viele NoSQL-Datenbanken oder eine DB-per-Microservice-Architektur), muss Ihre Anwendung diese Lücke füllen und diese Garantien bieten.

  • Dies ist nicht unmöglich zu tun, aber schwierig, es richtig zu machen. Sehr trickreich. Besonders in einer verteilten Umgebung, in der es mehrere Writer für jede Datenbank gibt. Diese Schwierigkeit führt zu einer hohen Wahrscheinlichkeit von Fehlern, die möglicherweise verworfene Daten, inkonsistente Daten usw. umfassen.

    Lesen Sie beispielsweise die Jepsen-Analysen bekannter verteilter Datenbanksysteme , möglicherweise beginnend mit der Analyse von Cassandra . Ich verstehe die Hälfte dieser Analyse nicht, aber die TL; DR ist, dass verteilte Systeme so schwierig sind, dass selbst branchenführende Projekte sie manchmal in einer Weise falsch verstehen, die im Nachhinein offensichtlich erscheinen kann.

  • Verteilte Systeme bedeuten auch einen größeren Entwicklungsaufwand. Bis zu einem gewissen Grad besteht ein direkter Kompromiss zwischen den Entwicklungskosten und dem Verlust von Geld für leistungsfähigere Hardware.

Beispiel: baumelnde Referenzen

In der Praxis sollten Sie sich nicht mit Informatik befassen, sondern mit Ihren geschäftlichen Anforderungen, um festzustellen, ob und wie ACID gelockert werden kann. Beispielsweise sind viele Fremdschlüsselbeziehungen möglicherweise nicht so wichtig, wie sie scheinen. Betrachten Sie eine Beziehung zwischen Produkt und Kategorie n: m. In einem RDBMS verwenden wir möglicherweise eine Fremdschlüsseleinschränkung, sodass nur vorhandene Produkte und vorhandene Kategorien Teil dieser Beziehung sein können. Was passiert, wenn wir separate Produkt- und Kategoriedienstleistungen einführen und ein Produkt oder eine Kategorie gelöscht wird?

In diesem Fall ist dies möglicherweise kein großes Problem, und wir können unsere Anwendung so schreiben, dass nicht mehr vorhandene Produkte oder Kategorien herausgefiltert werden. Aber es gibt Kompromisse!

  • Beachten Sie, dass dies möglicherweise eine Anwendungsebene JOINfür mehrere Datenbanken / Mikrodienste erfordert , wodurch lediglich die Verarbeitung vom Datenbankserver auf Ihre Anwendung verlagert wird. Dies erhöht die Gesamtlast und muss zusätzliche Daten über das Netzwerk übertragen.

  • Dies kann mit der Paginierung zu schaffen machen. Sie fordern beispielsweise die nächsten 25 Produkte aus einer Kategorie an und filtern nicht verfügbare Produkte aus dieser Antwort heraus. Jetzt zeigt Ihre Anwendung 23 Produkte an. Theoretisch wäre auch eine Seite mit Nullprodukten möglich!

  • Sie möchten gelegentlich ein Skript ausführen, mit dem herabhängende Referenzen entweder nach jeder relevanten Änderung oder in regelmäßigen Abständen bereinigt werden. Beachten Sie, dass solche Skripte ziemlich teuer sind, da sie jedes Produkt / jede Kategorie von der Hintergrunddatenbank / dem Mikroservice anfordern müssen, um festzustellen, ob es noch vorhanden ist.

  • Dies sollte offensichtlich sein, aber aus Gründen der Übersichtlichkeit: Verwenden Sie IDs nicht erneut. Autoincrement-ähnliche IDs sind möglicherweise in Ordnung oder nicht. GUIDs oder Hashes geben Ihnen mehr Flexibilität, z. B. indem Sie eine ID zuweisen können, bevor der Artikel in eine Datenbank eingefügt wird.

Beispiel: gleichzeitige Bestellungen

Betrachten Sie stattdessen eine Beziehung zwischen Produkt und Bestellung. Was passiert mit einer Bestellung, wenn ein Produkt gelöscht oder geändert wird? Ok, wir können einfach die relevanten Produktdaten in den Auftragseingang kopieren, um sie verfügbar zu halten. Aber was ist, wenn sich der Preis des Produkts ändert oder das Produkt nicht mehr verfügbar ist, bevor eine Bestellung für dieses Produkt aufgegeben wird? In einem verteilten System dauert die Verbreitung von Effekten einige Zeit, und die Bestellung wird wahrscheinlich veraltete Daten enthalten.

Wie Sie dies angehen, hängt wiederum von Ihren Geschäftsanforderungen ab. Möglicherweise ist die veraltete Bestellung akzeptabel und Sie können die Bestellung später stornieren, wenn sie nicht erfüllt werden kann.

Aber vielleicht ist das keine Option, z. B. für sehr gleichzeitige Einstellungen. Stellen Sie sich vor, dass 3000 Personen innerhalb der ersten 10 Sekunden Konzertkarten kaufen, und nehmen wir an, dass eine Änderung der Verfügbarkeit 10 ms erfordert, um sich zu verbreiten. Wie hoch ist die Wahrscheinlichkeit, dass das letzte Ticket an mehrere Personen verkauft wird? Hängt davon ab, wie mit diesen Kollisionen umgegangen wird. Bei Verwendung einer Poisson-Verteilung besteht λ = 3000 / (10s / 10ms) = 3jedoch die P(k > 1) = 1 - P(k = 0) - P(k = 1) = 80%Möglichkeit einer Kollision pro 10-ms-Intervall. Ob der Verkauf und die spätere Stornierung eines Großteils Ihrer Bestellungen ohne Betrug möglich ist, kann zu einem interessanten Gespräch mit Ihrer Rechtsabteilung führen.

Pragmatismus bedeutet, die besten Eigenschaften herauszusuchen.

Die gute Nachricht ist, dass Sie nicht auf ein verteiltes Datenbankmodell umsteigen müssen, wenn dies nicht anders erforderlich ist. Niemand wird Ihre Mitgliedschaft im Microservice Club kündigen, wenn Sie Microservices nicht „ordnungsgemäß“ durchführen, da es keinen solchen Club gibt - und keine echte Möglichkeit, Microservices aufzubauen.

Pragmatismus gewinnt jedes Mal. Kombinieren Sie verschiedene Ansätze, um Ihr Problem zu lösen. Dies könnte sogar Mikrodienste mit einer zentralisierten Datenbank bedeuten. Machen Sie sich nicht die Qualen verteilter Datenbanken zunutze, wenn Sie das nicht müssen.

Sie können ohne Microservices skalieren.

Microservices haben zwei Hauptvorteile:

  • Der organisatorische Vorteil besteht darin, dass sie von separaten Teams unabhängig entwickelt und bereitgestellt werden können (was wiederum erfordert, dass die Dienste eine stabile Schnittstelle bieten).
  • Der betriebliche Vorteil, dass jeder Mikrodienst unabhängig skaliert werden kann .

Wenn keine unabhängige Skalierung erforderlich ist, sind Mikrodienste viel weniger attraktiv.

Ein Datenbankserver ist bereits eine Art Dienst, den Sie (etwas) unabhängig skalieren können, z. B. durch Hinzufügen von Leserepliken. Sie erwähnen gespeicherte Prozeduren. Das Reduzieren kann einen so großen Effekt haben, dass andere Skalierbarkeitsdiskussionen umstritten sind.

Und es ist durchaus möglich, einen skalierbaren Monolithen zu haben, der alle Dienste als Bibliotheken enthält. Sie können dann skalieren, indem Sie mehr Instanzen des Monolithen starten, was natürlich voraussetzt, dass jede Instanz zustandslos ist.

Dies funktioniert in der Regel gut, bis der Monolith zu groß ist, um angemessen bereitgestellt zu werden, oder wenn für einige Dienste spezielle Ressourcenanforderungen gelten, sodass Sie sie möglicherweise unabhängig voneinander skalieren möchten. Die Problemdomänen, für die zusätzliche Ressourcen erforderlich sind, enthalten möglicherweise kein separates Datenmodell.

Haben Sie ein starkes Geschäftsmodell?

Sie kennen die Geschäftsanforderungen Ihres Unternehmens und können daher auf der Grundlage einer Analyse ein Argument für eine Datenbank-pro-Mikroservice-Architektur erstellen:

  • dass eine bestimmte Skalierung erforderlich ist und diese Architektur der kostengünstigste Ansatz ist, um diese Skalierbarkeit zu erzielen, unter Berücksichtigung des erhöhten Entwicklungsaufwands für eine solche Einrichtung und für alternative Lösungen; und
  • dass Ihre geschäftlichen Anforderungen eine Lockerung der relevanten ACID-Garantien ermöglichen, ohne dass dies zu verschiedenen Problemen wie den oben diskutierten führt.

Wenn Sie dies jedoch nicht nachweisen können, insbesondere wenn das aktuelle Datenbankdesign eine ausreichende Skalierung für die Zukunft unterstützt (wie Ihre Kollegen anscheinend glauben), haben Sie auch Ihre Antwort.

Es gibt auch eine große YAGNI-Komponente für die Skalierbarkeit. Angesichts der Unsicherheit ist es eine strategische Geschäftsentscheidung, jetzt auf Skalierbarkeit zu setzen (geringere Gesamtkosten, die jedoch Opportunitätskosten mit sich bringen und möglicherweise nicht erforderlich sind), im Gegensatz zu einer Verschiebung einiger Arbeiten zur Skalierbarkeit (höhere Gesamtkosten, falls erforderlich, aber bessere) Idee des tatsächlichen Maßstabs). Dies ist nicht in erster Linie eine technische Entscheidung.


Ausgezeichnete Antwort, danke. Können Sie diese Aussage näher erläutern? Meinen Sie damit, dass die Reduzierung der Anzahl der Verfahren einen großen Einfluss auf die Leistung haben könnte? Sie erwähnen gespeicherte Prozeduren. Das Reduzieren kann einen so großen Effekt haben, dass alle anderen Skalierbarkeitsdiskussionen umstritten sind.
Alan

1
@alan Gespeicherte Prozeduren können für immer verwendet werden, sie stellen jedoch zwei Leistungsprobleme dar: (1) Komplexere Abfragen sind für die Datenbank schwieriger zu optimieren. (2) Sprocs zu verwenden bedeutet, mehr Arbeit auf dem DB-Server zu erledigen. OP möchte die Datenbank aufteilen, um eine weitere Skalierung zu erreichen, aber die Vermeidung komplizierter Sprocs bietet möglicherweise bereits diesen Spielraum. Natürlich können Sprocs und komplexe Abfragen auch für die Leistung von Vorteil sein, z. B. wenn sie die Datenmenge minimieren, die für eine Abfrageantwort aus der Datenbank übertragen werden muss. Das Aufteilen der Datenbank würde dieses Problem verschlimmern, wenn serverübergreifende JOINs erforderlich sind.
Amon

0

Ich halte beide Ansätze für plausibel. Sie können wählen, ob Sie die Skalierbarkeit erhalten möchten, indem Sie die Vorteile von ACID- und monolithischen Datenbanken nutzen, die aktuelle Architektur beibehalten und die Skalierbarkeit und Agilität einer verteilten Architektur aufgeben. Die richtige Entscheidung wird sich aus dem aktuellen Geschäftsmodell und der Buz-Strategie für die nächsten Jahre ergeben. Rein aus technologischer Sicht gibt es Probleme, die es monolithisch halten und zu einem verteilten Ansatz übergehen. Ich würde das System analysieren und herausfinden, welche Anwendungen / Module / Geschäftsprozesse für die Skalierung kritischer sind, und die Risiken, Kosten und Vorteile bewerten, um diejenigen zu bestimmen, die in der monolithischen Architektur warten oder fortfahren sollen.


-1

Ihre Haltung ist plausibel und richtig.

Eine andere Frage ist jedoch, wie man eingefärbte Db-Süchtige überzeugen kann. Ich würde sagen, Sie haben zwei Möglichkeiten.

  1. Finden Sie ein konkretes Beispiel, an dem die DB an ihre Grenzen stößt. Haben Sie zum Beispiel 'Archivtabellen'? Warum ist das ok Was ist die maximale Anzahl von Bestellungen pro Sekunde, die Sie annehmen können? usw. Zeigen Sie, dass die Datenbank die Anforderungen nicht erfüllt und Ihre Lösung sie behebt.

  2. Stellen Sie teure Vertragspartner ein, um die beste Lösung zu finden. Weil sie teuer sind und Blogs haben, wird ihnen jeder glauben


1
Ich bin nicht die -1, aber damit dies eine gute Antwort ist, würde ich den Snark von Punkt 2 verlieren und erweitern, wenn es angebracht wäre, mehrere Datenbanken zu benötigen. Ich denke nicht, dass Archivtabellen notwendigerweise ein Gegenmuster in Datenbanken sind, die keine Partitionierung unterstützen. Die Datenbank, die ich derzeit verwende, verfügt über ca. 130 GB Daten mit 26 Tabellen und mehr als 10 Millionen Zeilen. Die Leistung ist nicht remote genug, um die Datenbank aufteilen zu können. Ich bin sehr skeptisch und würde gerne erfahren, warum dies eine gute Idee ist und wann dies getan werden muss - diese Antwort ist die naheliegendste, die ich bisher gesehen habe.
Mcottle

Gut. Ich erwähne Archivtabellen, weil sie FK-Einschränkungen aufheben. Es ist ein Riss in der Rüstung. Aufteilen durch microservice ist keine Sache von db. Es ist eine Sache, die Ihre microservices trennbar macht. Wenn Sie einen nicht ausschalten und wegwerfen können, handelt es sich nicht wirklich um einen Microservice. Zu Punkt 2 erwähnt das OP MF, sie könnten ihn buchstäblich einstellen / Gedankenarbeiten anstellen, um zu kommen und ihnen zu sagen, dass sie die Datenbank aufteilen sollen
Ewan

"Wenn Sie einen nicht ausschalten und wegwerfen können, ist es kein richtiger Microservice." Dies gilt für den Dienst selbst, ist jedoch nicht unbedingt ein Argument dafür, warum der Dienst eine eigene Datenbank benötigt. Letztendlich ist die Datenbank selbst ein Dienst, der vom Mikrodienst verwendet wird. Der Mikrodienst weiß oder kümmert sich nicht wirklich darum, ob sich die von ihm verwendeten Daten in einer separaten Datenbank oder einer gemeinsam genutzten Datenbank befinden. Sie können Kopien dieses Mikrodienstes nach oben oder unten drehen, und nichts ändert sich wirklich.
Chris Pratt

Das beste Argument für "Datenbank pro Dienst" sind Verbindungsbeschränkungen. Es ist nicht ungewöhnlich, dass das Verbindungspooling verwendet wird. Daher erfordert jeder Mikroservice bereits mehrere Verbindungen zur Datenbankinstanz. Sie können dann mehrere Instanzen jedes dieser Mikroservices mit jeweils eigenen Pools einrichten. Irgendwann könnten sich die Dinge zuspitzen, und Sie haben einfach die Fähigkeit der Datenbank erschöpft, alle Verbindungen zu verarbeiten, die sie erhält.
Chris Pratt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.