Warum ist es so schlecht, Daten aus einer Datenbank zu lesen, die einem anderen Mikrodienst gehört?

64

Ich habe kürzlich diesen ausgezeichneten Artikel über die Microservice-Architektur gelesen: http://www.infoq.com/articles/microservices-intro

Wenn Sie eine Webseite auf Amazon laden, arbeiten mehr als 100 Microservices zusammen, um diese Seite bereitzustellen.

In diesem Artikel wird beschrieben, dass die gesamte Kommunikation zwischen Microservices nur über eine API erfolgen kann. Meine Frage ist, warum es so schlimm ist zu sagen, dass alle Datenbank-Schreibvorgänge nur über eine API erfolgen können, aber Sie können direkt aus den Datenbanken der verschiedenen Mikrodienste lesen. Man könnte zum Beispiel sagen, dass nur wenige Datenbankansichten außerhalb des Mikrodienstes zugänglich sind, sodass das Team, das den Mikrodienst verwaltet, weiß, dass es, solange diese Ansichten intakt bleiben, die Datenbankstruktur seines Mikrodienstes so stark wie möglich ändern kann wollen.

Vermisse ich hier etwas? Gibt es einen anderen Grund, warum Daten nur über eine API gelesen werden sollten?

Es erübrigt sich zu erwähnen, dass mein Unternehmen bedeutend kleiner als Amazon ist (und dies auch immer sein wird) und die maximale Anzahl von Nutzern, die wir jemals haben können, bei etwa 5 Millionen liegt.

David
quelle
Ein allgemeinerer Faktor, der in den Antworten nicht erwähnt wird, ist, dass beim Schreiben in die Datenbank durch lokales Caching, selbst durch einfaches O / R-Mapping, veraltete Daten beim sofortigen Zugriff auf die Datenbank entstehen können. Wenn Sie erwägen, die µservice-API aus Geschwindigkeitsgründen zu umgehen, ist die µservice-Architektur möglicherweise zu weit gegangen.
Joop Eggen
Wenn Datenbanklesevorgänge zugelassen werden, werden alle Details der Datenbank Teil der öffentlichen API. Ich würde die Kompatibilität mit einer so komplexen API nicht beibehalten wollen.
Patrick
Aber wird in diesem Fall die Ansicht nicht einfach, zumindest aus semantischen Gründen, Teil der API? Es ist nur eine Frage dessen, wie Sie die API aufrufen und was Sie dazu zwingen zu warten. (Normalerweise ist es einfacher, eine Ebene über der Datenbank konsistent zu halten.)
lc.
Ich stimme zu, dass die Ansicht einfach eine Form einer API wäre. Ich denke, die meisten Leute, die diese Frage beantworten, lesen meine Idee, Ansichten als Abstraktionsebene zu verwenden, nicht. Ich verstehe zwar, dass es eine große Herausforderung wäre, die Ansicht beizubehalten, wenn man die Datenbanktechnologie ändern würde, aber ich bin bereit, darauf zu wetten, dass wir unsere Datenbanktechnologie in den nächsten 5 Jahren nicht ändern müssen. Auch bei nur 5 Mill-Benutzern ist die Leistung kein Problem. In Anbetracht unserer Größe danke ich, dass ich mich für diese Lösung entscheiden werde, obwohl die Antworten hier darauf hinweisen, dass ich direkt in eine Welt des Schmerzes vordrehe.
David

Antworten:

69

Datenbanken sind nicht sehr gut darin, Informationen zu verbergen, was durchaus plausibel ist, da es ihre Aufgabe ist, Informationen tatsächlich preiszugeben. Aber das macht sie zu einem miesen Werkzeug, wenn es um das Einkapseln geht. Warum möchten Sie eine Verkapselung?

Szenario: Sie binden mehrere Komponenten direkt an ein RDBMS und sehen, dass eine bestimmte Komponente zu einem Engpass für die Leistung wird, für den Sie möglicherweise die Datenbank denormalisieren möchten. Dies ist jedoch nicht möglich, da alle anderen Komponenten betroffen wären. Möglicherweise stellen Sie sogar fest, dass Sie mit einem Dokumentenspeicher oder einer Diagrammdatenbank besser zurechtkommen als mit einem RDBMS. Wenn die Daten von einer kleinen API gekapselt werden, haben Sie eine realistische Chance, diese API nach Bedarf erneut zu implementieren. Sie können transparent Cache-Ebenen einfügen und was nicht.

Das Fummeln mit der Speicherschicht direkt von der Anwendungsschicht ist das diametrale Gegenteil von dem, was das Prinzip der Abhängigkeitsinversion vorschlägt.

back2dos
quelle
1
Das ist ein großartiger Punkt! Eine Sache, die mich weniger beunruhigt, ist, dass Postgres jetzt sowohl Document Store als auch RDBMS unterstützt ( withouttheloop.com/articles/2014-09-30-postgresql-nosql ). Ihr Punkt ist jedoch immer noch gültig, und ich werde ihn sorgfältig prüfen.
David
3
Es sollte Sie nicht weniger beunruhigen, @David, nur weil ein Tool etwas kann, heißt das nicht, dass eine Änderung nicht viele Dinge kaputt macht. Das ist der Punkt, an dem es darum geht, einen gewissen Grad an Trennung zu erreichen. Sie können die Daten hinter einer API vollständig ändern, ohne das zu ändern, was der Benutzer sieht. Ich spreche hier als Datenbasis ... solange der Kunde das Gleiche sieht, können Sie das Backend so oft ändern, wie Sie möchten.
Ben
1
@ David Während dies interessante Neuigkeiten sind, ist es für das von mir beschriebene Szenario ziemlich irrelevant. Wenn Sie Ihr DB-Schema von einem relationalen zu einem dokumentbasierten Schema ändern, hat dies die gleichen Auswirkungen auf alle davon abhängigen: Sie müssen alle Abfragen neu schreiben. Es ist auch ein Albtraum, alle diese Änderungen gleichzeitig implementieren zu müssen, damit die Kompatibilität zwischen den Komponenten im gesamten System erhalten bleibt.
back2dos
1
@David: Wenn Sie den Zugriff auf einige genau definierte Ansichten einschränken, müssen Sie möglicherweise eine andere API mit einigen der damit verbundenen Vorteile erstellen. Solange es sich nur um Ansichten handelt, sind Sie jedoch auf den schreibgeschützten Zugriff beschränkt. Da eine Komponente sowohl von der Service-API als auch von der Ansichts-API abhängt, ist sie sehr anfällig. Wenn Sie also eine Komponente von den Ansichten abhängig machen, haben Sie sie als schreibgeschützt oder wartungsintensiv festgelegt. Ich rieche hier technische Schulden. Möglicherweise möchten Sie auch eine horizontale Partitionierung hinzufügen, die Ihre Datenbank nicht zulässt.
back2dos
2
@David: Auf lange Sicht ist es wichtiger, wie einfach es ist, Code zu ändern, als wie einfach es ist, ihn zu schreiben. Architektur sagt nicht "Sie sollten Code nicht so und so schreiben", es sagt "wenn Sie das tun, werden Sie schreckliche Alpträume erleiden, die versuchen, ihn zu pflegen". Wenn es sich um Prototypen handelt, ist keine Wartung erforderlich. Gehen Sie geradeaus. Ein Prototyp sollte sich so einfach wie möglich bewähren. Wenn Sie jedoch versuchen, alle bewährten Punkte in ein System zu integrieren, ohne dass sich dies auf die selbst verursachte Folter durch Sisyphus auswirkt, sollten Sie die Extrameile gehen.
back2dos
55

Was ist wichtiger und wichtiger an einem Mikrodienst: seine API oder sein Datenbankschema? Die API, denn das ist ihr Vertrag mit dem Rest der Welt. Das Datenbankschema ist einfach eine bequeme Methode zum Speichern der vom Dienst verwalteten Daten, die hoffentlich so organisiert sind, dass die Leistung des Mikrodienstes optimiert wird. Das Entwicklungsteam sollte jederzeit die Möglichkeit haben, dieses Schema zu reorganisieren oder auf eine völlig andere Datenspeicherlösung zu wechseln. Der Rest der Welt sollte sich nicht darum kümmern. Der Rest der Welt kümmert sich, wenn sich die API ändert, da die API der Vertrag ist.

Nun, wenn Sie in ihre Datenbank spähen gehen

  • Sie fügen eine unerwünschte Abhängigkeit von ihrem Schema hinzu. Sie können es nicht ändern, ohne sich auf Ihren Service auszuwirken.
  • Sie fügen ihren Interna eine unerwünschte und unvorhersehbare Last hinzu.
  • Die Leistung Ihres eigenen Dienstes wird von der Leistung ihrer Datenbank beeinflusst (sie werden versuchen, ihren Dienst so zu optimieren , dass er für Kunden und ihre Datenbank nur für ihren Dienst gut funktioniert).
  • Sie binden Ihre Implementierung an ein Schema, das die Ressourcen in ihrem Datenspeicher möglicherweise nicht genau und eindeutig darstellt. Es enthält möglicherweise zusätzliche Details, die nur zum Verfolgen des internen Status oder zum Erfüllen der jeweiligen Implementierung erforderlich sind (was Sie nicht interessieren sollten).
  • Sie können den Status ihres Dienstes unwissentlich zerstören oder beschädigen (und sie werden nicht wissen, dass Sie dies tun)
  • Sie können Ressourcen aktualisieren, löschen oder aus ihrer Datenbank entfernen, ohne dass diese davon erfahren.

Die letzten beiden Punkte können möglicherweise nicht auftreten, wenn Sie nur Lesezugriff erhalten, die anderen Punkte sind jedoch mehr als ein guter Grund. Gemeinsame Datenbanken sind eine schlechte Sache.

Für weniger erfahrene Entwickler (oder diejenigen, die nicht lernen) ist es üblich, die Datenbank als wichtiger als den Service zu betrachten, die Datenbank als die reale Sache und den Service nur als einen Weg, dorthin zu gelangen. Das ist der falsche Weg.

itsbruce
quelle
4
Bitte beachten Sie, dass alle oben genannten Punkte auch dann gültig sind, wenn Sie der einzige Entwickler sind, der am gesamten Projekt arbeitet. Wenn Sie ein komplexes Projekt selbst verwalten, werden all diese Bedenken angesprochen.
itsbruce
15

Microservice Architecture ist schwer zu beschreiben, aber die beste Möglichkeit, darüber nachzudenken, ist eine Verbindung zwischen komponentenorientierter Architektur und serviceorientierter Architektur. Software als Suite besteht aus vielen Kleinunternehmenskomponenten mit einer ganz bestimmten Geschäftsdomänenverantwortung. Ihre Schnittstelle zur Außenwelt entweder in bereitgestellten Diensten oder in erforderlichen Diensten erfolgt über eine API klar definierter Dienste.

Das Schreiben in und sogar das Lesen aus einer Datenbank außerhalb des Geschäftsbereichs Ihrer Komponenten widerspricht diesem Architekturstil.

Der Hauptgrund dafür ist, dass eine API, die über einen Dienst von einer anderen Softwarekomponente bereitgestellt wird, die begründete Erwartung hat, dass die API höchstwahrscheinlich abwärtskompatibel ist, wenn neue Versionen der Dienstbereitstellungskomponente verfügbar werden. Wenn ich Entwickler einer "Providing" -Komponente bin, muss ich mich nur um die Abwärtskompatibilität mit meiner API kümmern. Wenn ich weiß, dass es drei andere Entwicklungsteams gibt, die benutzerdefinierte Abfragen direkt für meine Datenbank geschrieben haben, ist mein Job viel komplizierter geworden.

Schlimmer noch, vielleicht ist das andere Team, das diese geschrieben hat, mitten im Sprint in einem kritischen Projekt und kann diese Änderung von Ihrer Komponente jetzt nicht akzeptieren. Jetzt wird die Softwareentwicklung für Ihre Komponente in einer Geschäftsdomäne, die Sie besitzen, durch die Entwicklung in einer anderen Geschäftsdomäne vorangetrieben.

Die vollständige Interaktion durch Dienste verringert die Kopplung zwischen verschiedenen Softwarekomponenten, sodass Situationen wie diese nicht so häufig auftreten. Wenn es um andere Komponenten geht, die eine Ansicht in der Datenbank verwenden, haben Sie mehr Möglichkeiten, die Ansicht abwärtskompatibel zu machen, wenn andere Benutzer Abfragen dazu geschrieben haben. Ich bin jedoch immer noch der Meinung, dass dies der Ausnahmefall sein und nur für die Berichterstellung oder Stapelverarbeitung erfolgen sollte, bei der eine Anwendung enorme Datenmengen einlesen muss.

Dies funktioniert natürlich gut in großen verteilten Teams, in denen Entwicklungsteams nach Geschäftsbereichen wie Amazon getrennt sind. Wenn Sie ein kleiner Entwicklungsbetrieb sind, können Sie dennoch von diesem Modell profitieren, insbesondere, wenn Sie sich schnell auf ein großes Projekt vorbereiten müssen, aber auch, wenn Sie mit Hersteller-Software zu tun haben.

maple_shaft
quelle
4

In den letzten 20 Jahren habe ich einige große modulare Datenbankentwürfe gesehen, und ich habe das von David vorgeschlagene Szenario einige Male gesehen, in denen Anwendungen Schreibzugriff auf ihr eigenes Schema / ihre eigenen Tabellen und Lesezugriff auf ein anderes Schema / haben. Reihe von Tabellen. Am häufigsten können diese Daten, auf die eine Anwendung / ein Modul nur Lesezugriff erhält, als "Stammdaten" bezeichnet werden .

In dieser Zeit habe ich nicht die Probleme gesehen, die mir frühere Antworten nahe legen, und ich denke, es lohnt sich, die in den vorherigen Antworten angesprochenen Punkte genauer zu betrachten.

Szenario: Sie binden mehrere Komponenten direkt an ein RDBMS, und Sie sehen, dass eine bestimmte Komponente zu einem Performance-Engpass wird

Ich bin mit diesem Kommentar einverstanden, mit der Ausnahme, dass dies auch ein Argument dafür ist, eine Kopie der Daten lokal für den Microservice zu lesen. Das heißt, die meisten ausgereiften Datenbanken unterstützen die Replikation. Daher können die "Stammdaten" ohne Entwickleraufwand physisch in die Microservice-Datenbank repliziert werden, wenn dies gewünscht oder erforderlich ist.

Einige erkennen dies in älteren Versionen möglicherweise als "Unternehmensdatenbank", die Kerntabellen in eine "Abteilungsdatenbank" repliziert. Ein Punkt hier ist, dass es im Allgemeinen gut ist, wenn eine Datenbank dies für uns mit eingebauter Replikation geänderter Daten tut (nur Deltas, in binärer Form und zu minimalen Kosten für die Quellendatenbank).

Wenn unsere Datenbankauswahl diese Standardreplikationsunterstützung nicht zulässt, können wir in eine Situation geraten, in der wir "Stammdaten" in die Microservice-Datenbanken verschieben möchten, was zu einem erheblichen Aufwand für Entwickler führen kann auch ein wesentlich weniger effizienter Mechanismus sein.

Möglicherweise möchten Sie die Datenbank denormalisieren, dies ist jedoch nicht möglich, da alle anderen Komponenten betroffen wären

Für mich ist diese Aussage einfach nicht richtig. Die Denormalisierung ist eine "additive" Änderung und keine "brechende Änderung", und keine Anwendung sollte aufgrund der Denormalisierung brechen.

Die einzige Möglichkeit, eine Anwendung zu unterbrechen, besteht darin, dass der Anwendungscode so etwas wie "select * ..." verwendet und keine zusätzliche Spalte verarbeitet. Für mich wäre das ein Fehler in der Anwendung?

Wie kann eine Denormalisierung eine Anwendung stören? Klingt für mich nach FUD.

Schemaabhängigkeit:

Ja, die Anwendung ist jetzt vom Datenbankschema abhängig, was impliziert, dass dies ein großes Problem sein sollte. Obwohl das Hinzufügen zusätzlicher Abhängigkeiten offensichtlich nicht ideal ist, ist es meiner Erfahrung nach kein Problem, eine Abhängigkeit vom Datenbankschema zu haben. Warum könnte dies der Fall sein? Habe ich gerade Glück gehabt?

Stammdaten

Das Schema, auf das ein Microservice normalerweise nur Lesezugriff haben soll, ist das, was ich als " Stammdaten " für das Unternehmen bezeichnen würde. Es verfügt über die Kerndaten, die für das Unternehmen wesentlich sind.

Historisch bedeutet dies, dass das Schema, von dem wir die Abhängigkeit hinzufügen, sowohl ausgereift als auch stabil ist (etwas grundlegend für das Unternehmen und unveränderlich).

Normalisierung

Wenn 3 Datenbankdesigner ein normalisiertes Datenbankschema entwerfen, erhalten sie dasselbe Design. Ok, es könnte einige 4NF / 5NF-Variationen geben, aber nicht viel. Außerdem gibt es eine Reihe von Fragen, die der Designer stellen kann, um das Modell zu validieren, damit der Designer sicher sein kann, dass er zu 4NF gekommen ist (Bin ich zu optimistisch? Haben die Leute Probleme, zu 4NF zu gelangen?).

update: Mit 4NF meine ich hier, dass alle Tabellen im Schema bis zu 4NF auf ihre höchste Normalform gebracht wurden (alle Tabellen wurden bis zu 4NF entsprechend normalisiert).

Ich glaube, der Normalisierungsentwurfsprozess ist der Grund, warum Datenbankdesigner im Allgemeinen mit der Idee vertraut sind, von einem normalisierten Datenbankschema abhängig zu sein.

Durch den Normalisierungsprozess wird der DB-Entwurf zu einem bekannten "richtigen" Entwurf, und die Abweichungen von diesem sollten aus Leistungsgründen denormalisiert werden.

  1. Abhängig von den unterstützten DB-Typen können Abweichungen auftreten (JSON-, ARRAY-, Geo-Typ-Unterstützung usw.).
  2. Einige argumentieren möglicherweise für Variationen basierend auf 4NF / 5NF
  3. Wir schließen physische Variationen aus (weil das keine Rolle spielt)
  4. Wir beschränken dies auf den OLTP-Entwurf und nicht auf den DW-Entwurf, da dies die Schemata sind, auf die nur Lesezugriff gewährt werden soll

Wenn 3 Programmierer einen Entwurf zur Implementierung (als Code) erhalten würden, wären 3 verschiedene Implementierungen zu erwarten (möglicherweise sehr unterschiedlich).

Für mich liegt möglicherweise eine Frage des "Glaubens an die Normalisierung" vor.

Brechen Schemaänderungen?

Denormalisierung, Hinzufügen von Spalten, Ändern von Spalten für eine größere Speicherkapazität, Erweitern des Designs mit neuen Tabellen usw. sind allesamt ununterbrochene Änderungen, und DB-Designer, die die 4. Normalform erreicht haben, werden davon überzeugt sein.

Breaking Changes sind offensichtlich möglich, indem Sie Spalten / Tabellen löschen oder den Breaking Type ändern. Möglich ja, aber praktisch habe ich hier überhaupt keine Probleme gehabt. Vielleicht, weil verstanden wird, was bahnbrechende Veränderungen sind und welche gut gemanagt wurden?

Es würde mich interessieren, Fälle zu hören, in denen Schemaänderungen im Kontext von gemeinsam genutzten Nur-Lese-Schemas unterbrochen werden.

Was ist wichtiger und wichtiger an einem Mikrodienst: seine API oder sein Datenbankschema? Die API, denn das ist ihr Vertrag mit dem Rest der Welt.

Obwohl ich dieser Aussage zustimme, gibt es meiner Meinung nach eine wichtige Einschränkung, die wir von einem Enterprise Architect hören könnten: "Daten leben für immer" . Das heißt, während die API das Wichtigste sein könnte, sind die Daten auch für das gesamte Unternehmen ziemlich wichtig, und sie werden für eine sehr lange Zeit wichtig sein.

Wenn beispielsweise das Data Warehouse für Business Intelligence ausgefüllt werden muss, werden die Schema- und CDC-Unterstützung aus Sicht der Geschäftsberichte unabhängig von der API wichtig.

Probleme mit APIs?

Wenn APIs nun perfekt und einfach wären, wären alle Punkte umstritten, da wir immer eine API wählen würden, anstatt nur lokalen Lesezugriff zu haben. Die Motivation, auch nur den lokalen Lesezugriff in Betracht zu ziehen, besteht darin, dass möglicherweise Probleme bei der Verwendung von APIs auftreten, die durch den lokalen Zugriff vermieden werden.

What motivates people to desire local read-only access?

API-Optimierung:

LinkedIn hat eine interessante Präsentation (ab 2009) zum Thema der Optimierung ihrer API und warum es ihnen auf ihrer Skala wichtig ist. http://www.slideshare.net/linkedin/building-consistent-restful-apis-in-a-highperformance-environment

Kurz gesagt, sobald eine API viele verschiedene Anwendungsfälle unterstützen muss, kann sie leicht in die Situation geraten, in der sie einen Anwendungsfall optimal und den Rest aus Netzwerk- und Datenbanksicht eher schlecht unterstützt.

Wenn die API nicht die gleiche Komplexität aufweist wie LinkedIn, können Sie problemlos die folgenden Szenarien abrufen:

  • Die API ruft viel mehr Daten ab, als Sie benötigen (verschwenderisch)
  • Chatty APIs, bei denen Sie die API oft aufrufen müssen

Ja, wir können natürlich das Cachen zu APIs hinzufügen, aber letztendlich ist der API-Aufruf ein Remote-Aufruf und es gibt eine Reihe von Optimierungen, die Entwicklern zur Verfügung stehen, wenn die Daten lokal sind.

Ich vermute, es gibt eine Reihe von Leuten, die es so zusammenfassen könnten:

  • Kostengünstige Replikation von Stammdaten in die Microservice-Datenbank (ohne Entwicklungskosten und technisch effizient)
  • Vertrauen in die Normalisierung und die Widerstandsfähigkeit von Anwendungen gegenüber Schemaänderungen
  • Möglichkeit, jeden Anwendungsfall einfach zu optimieren und potenziell gesprächige, verschwenderische und ineffiziente Remote-API-Aufrufe zu vermeiden
  • Plus einige andere Vorteile in Bezug auf Einschränkungen und kohärentes Design

Diese Antwort ist viel zu lang. Entschuldigung !!

Rob Bygrave
quelle
Durch das Hinzufügen einer Spalte werden Anwendungen im Allgemeinen unterbrochen. Wenn Sie "Gehalt" haben, bricht die Anwendung, die alle Gehälter summiert, ab, wenn eine neue Spalte "salary_currency" eingeführt wird.
Kubanczyk
"Ja wirklich?" Kommt auf deine Definition von "Pausen" an, nehme ich an. Wenn die App wie erwartet ohne "salary_currency" in Produktion und Betrieb wäre, warum würdest du sie dann als defekt betrachten?
Rob Bygrave
Die Anwendung wird ohne Fehler ausgeführt und zeigt eine Nummer an. Aber es ist sinnlos. Wenn der CEO feststellt, dass die Summe der Gehälter für den letzten Monat 6 Millionen statt 50 Tausend beträgt (wegen eines neuen Mitarbeiters, der in südkoreanischen Wons bezahlt wird), wird die Definition eines nützlichen / nutzlosen Outputs nicht viel diskutiert.
Kubanczyk
0

Die Statusverwaltung (möglicherweise eine Datenbank) kann im Container des Microservice bereitgestellt und über eine API verfügbar gemacht werden. Die Datenbank eines Microservice ist für andere Systeme außerhalb des Containers nicht sichtbar - nur für die API. Alternativ kann ein anderer Dienst (z. B. ein Cache) den Status über eine API verwalten. Es ist ein wesentlicher Unterschied in der Architektur, dass alle Abhängigkeiten des Microservice (außer API-Aufrufen zu anderen Diensten) in einem einzigen bereitstellbaren Container enthalten sind. Wenn man das nicht bekommt, gehe zurück und studiere die Architektur.

Eric Roch
quelle