Kapitel 1. Jenseits relationaler Datenbanken

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Wenn die Idee anfangs nicht absurd ist, dann gibt es keine Hoffnung für sie.

Albert Einstein

Willkommen zu Cassandra: The Definitive Guide. Das Ziel dieses Buches ist es, Entwicklern und Datenbankadministratoren zu helfen, diese wichtige Datenbanktechnologie zu verstehen. Im Laufe dieses Buches werden wir herausfinden, wie Cassandra im Vergleich zu traditionellen relationalen Datenbankmanagementsystemen funktioniert, und wir werden dir helfen, sie in deiner eigenen Umgebung einzusetzen.

Was ist falsch an relationalen Datenbanken?

Wenn ich die Leute gefragt hätte, was sie wollen, hätten sie gesagt: schnellere Pferde.

Henry Ford

Wir bitten dich, ein bestimmtes Datenmodell zu betrachten, das von einem kleinen Team in einem Unternehmen mit Tausenden von Mitarbeitern erfunden wurde. Es war über eine TCP/IP-Schnittstelle zugänglich und konnte in einer Vielzahl von Sprachen genutzt werden, darunter Java und Webservices. Dieses Modell war anfangs nur für die fortgeschrittensten Informatiker/innen schwer zu verstehen, bis eine breitere Akzeptanz dazu beitrug, die Konzepte klarer zu machen. Die Nutzung der Datenbank, die auf diesem Modell aufbaut, erforderte das Erlernen neuer Begriffe und ein neues Denken über die Speicherung von Daten. Aber mit der Entwicklung neuer Produkte setzten immer mehr Unternehmen und Behörden die Datenbank ein, nicht zuletzt, weil sie schnell war - sie konnte Tausende von Operationen pro Sekunde verarbeiten. Der Umsatz, der damit erzielt wurde, war enorm.

Und dann kam ein neues Modell daher.

Das neue Modell war bedrohlich, vor allem aus zwei Gründen. Erstens unterschied sich das neue Modell stark von dem alten Modell, das es ausdrücklich in Frage stellte. Es war bedrohlich, weil es schwierig sein kann, etwas Neues und Anderes zu verstehen. Die daraus resultierenden Debatten können dazu beitragen, dass die Menschen hartnäckig in ihren Ansichten verharren - Ansichten, die sie vielleicht größtenteils aus dem Klima, in dem sie ihr Handwerk gelernt haben, und den Umständen, unter denen sie arbeiten, übernommen haben. Zweitens, und das ist vielleicht noch wichtiger, war das neue Modell eine Bedrohung, weil die Unternehmen erhebliche Investitionen in das alte Modell getätigt hatten und damit viel Geld verdienten. Ein Kurswechsel schien lächerlich, ja sogar unmöglich.

Die Rede ist natürlich von der hierarchischen Datenbank Information Management System (IMS), die 1966 bei IBM erfunden wurde.

Das IMS wurde für den Einsatz in der Saturn V Mondrakete gebaut. Ihr Architekt war Vern Watts, der ihr seine ganze Karriere gewidmet hat. Viele von uns sind mit IBMs Datenbank DB2 vertraut. Die sehr beliebte Datenbank DB2 von IBM ist der Nachfolger von DB1 - dem Produkt, das auf dem hierarchischen Datenmodell IMS aufbaut. IMS wurde 1968 auf den Markt gebracht und war später im Customer Information Control System (CICS) und anderen Anwendungen erfolgreich. Es wird auch heute noch verwendet.

Aber in den Jahren nach der Erfindung von IMS war das neue Modell, das disruptive Modell, das bedrohliche Modell, die relationale Datenbank.

In seinem Aufsatz "A Relational Model of Data for Large Shared Data Banks" (Ein relationales Datenmodell für große gemeinsam genutzte Datenbanken) aus dem Jahr 1970 entwickelte Dr. Edgar F. Codd, der im IBM-Forschungslabor in San Jose arbeitete, seine Theorie des relationalen Datenmodells weiter. Diese Arbeit wurde zur Grundlage für relationale Datenbankmanagementsysteme.

Codds Arbeit stand im Gegensatz zur hierarchischen Struktur von IMS. Um eine relationale Datenbank zu verstehen und mit ihr zu arbeiten, mussten neue Begriffe erlernt werden, darunter Relationen, Tupel und Normalform, die für die Benutzer von IMS sehr fremdartig geklungen haben müssen. Die relationale Datenbank bot gegenüber ihrer Vorgängerin einige entscheidende Vorteile, wie z. B. die Möglichkeit, komplexe Beziehungen zwischen mehreren Entitäten auszudrücken, die weit über das hinausgingen, was mit hierarchischen Datenbanken dargestellt werden konnte.

Obwohl sich diese Ideen und ihre Anwendung in vier Jahrzehnten weiterentwickelt haben, ist die relationale Datenbank immer noch eine der erfolgreichsten Softwareanwendungen der Geschichte. Sie wird in Form von Microsoft Access in Einzelunternehmen und in riesigen multinationalen Konzernen mit Clustern aus Hunderten von fein abgestimmten Instanzen verwendet, die Multiterabyte große Datenlager darstellen. Relationale Datenbanken speichern Rechnungen, Kundendatensätze, Produktkataloge, Buchhaltungsbücher, Benutzerauthentifizierungssysteme - die ganze Welt, wie es scheint. Es steht außer Frage, dass die relationale Datenbank ein wichtiger Bestandteil der modernen Technologie- und Unternehmenslandschaft ist und uns in ihren verschiedenen Formen noch viele Jahre begleiten wird, ebenso wie IMS in seinen verschiedenen Formen. Das relationale Modell stellt eine Alternative zu IMS dar, und beide haben ihre Berechtigung.

Die kurze Antwort auf die Frage "Was ist falsch an relationalen Datenbanken?" ist also "Nichts".

Es gibt aber auch eine etwas längere Antwort, die besagt, dass hin und wieder eine Idee geboren wird, die die Dinge angeblich verändert und eine Art Revolution auslöst. Auf der anderen Seite sind solche Revolutionen, strukturell betrachtet, einfach nur das übliche Geschäft der Geschichte. IMS, RDBMS, NoSQL. Das Pferd, das Auto, das Flugzeug. Jede von ihnen baut auf dem Stand der Technik auf, jede versucht, bestimmte Probleme zu lösen, und deshalb ist jede von ihnen gut in bestimmten Dingen - und weniger gut in anderen. Sie existieren nebeneinander, auch jetzt noch.

Betrachten wir also kurz, warum du eine Alternative zur relationalen Datenbank in Betracht ziehen könntest, so wie Codd selbst vor vier Jahrzehnten das Informationsmanagementsystem betrachtete und dachte, dass es vielleicht nicht der einzige legitime Weg ist, Informationen zu organisieren und Datenprobleme zu lösen, und dass es sich für bestimmte Probleme vielleicht als fruchtbar erweisen könnte, eine Alternative in Betracht zu ziehen.

Du stößt auf Skalierbarkeitsprobleme, wenn deine relationalen Anwendungen erfolgreich werden und die Nutzung steigt. Die Notwendigkeit, zusammenhängende Daten aus mehreren Tabellen über Joins zu sammeln, ist in jeder relativ normalisierten relationalen Datenbank von selbst bescheidener Größe gegeben, und Joins können langsam sein. Die Konsistenz von Datenbanken wird in der Regel durch Transaktionen erreicht, bei denen ein Teil der Datenbank gesperrt werden muss, damit er für andere Clients nicht verfügbar ist. Das kann bei sehr hoher Last unhaltbar werden, da die Sperren dazu führen, dass konkurrierende Nutzer/innen Schlange stehen und darauf warten, dass sie an der Reihe sind, die Daten zu lesen oder zu schreiben.

Normalerweise gehst du diese Probleme auf eine oder mehrere der folgenden Arten an, manchmal in dieser Reihenfolge:

  • Du kannst das Problem mit zusätzlicher Hardware angehen, indem du mehr Speicher, schnellere Prozessoren und bessere Festplatten einsetzt. Dies wird als vertikale Skalierung bezeichnet. Das kann dir eine Zeit lang Erleichterung verschaffen.

  • Wenn die Probleme wieder auftauchen, scheint die Antwort ähnlich zu sein: Jetzt, wo die eine Box ausgereizt ist, fügst du Hardware in Form von zusätzlichen Boxen in einem Datenbank-Cluster hinzu. Jetzt hast du das Problem der Datenreplikation und -konsistenz bei normaler Nutzung und in Failover-Szenarien. Dieses Problem hattest du vorher nicht.

  • Jetzt musst du die Konfiguration des Datenbankmanagementsystems aktualisieren. Das kann bedeuten, dass du die Kanäle optimierst, über die die Datenbank in das zugrunde liegende Dateisystem schreibt. Du schaltest die Protokollierung oder das Journaling aus, was häufig keine wünschenswerte (oder, je nach Situation, legale) Option ist.

  • Nachdem du dem Datenbanksystem so viel Aufmerksamkeit wie möglich geschenkt hast, wendest du dich deiner Anwendung zu. Du versuchst, deine Indizes zu verbessern. Du optimierst die Abfragen. Aber vermutlich hattest du in dieser Größenordnung noch keine Ahnung von Index- und Abfrageoptimierung und hattest sie bereits in einem ziemlich guten Zustand. So wird es zu einem mühsamen Prozess, den Datenzugriffscode zu durchforsten, um Möglichkeiten zur Feinabstimmung zu finden. Dazu kann es gehören, Joins zu reduzieren oder neu zu organisieren, ressourcenintensive Funktionen wie die XML-Verarbeitung innerhalb einer Stored Procedure zu streichen und so weiter. Wenn du die XML-Verarbeitung also irgendwo durchführen musst, verschiebst du das Problem in die Anwendungsschicht und hoffst, dass du es dort lösen kannst, und drückst die Daumen, dass du in der Zwischenzeit nicht etwas anderes kaputt machst.

  • Du verwendest eine Caching-Schicht. Bei größeren Systemen kann das ein verteilter Cache wie Redis, Memcached, Hazelcast, Aerospike, Ehcache oder Riak sein. Jetzt hast du ein Konsistenzproblem zwischen den Aktualisierungen im Cache und den Aktualisierungen in der Datenbank, das sich in einem Cluster noch verschärft.

  • Du wendest deine Aufmerksamkeit wieder der Datenbank zu und beschließt, dass du jetzt, wo die Anwendung erstellt ist und du die primären Abfragepfade verstehst, einige der Daten duplizieren kannst, damit sie mehr wie die Abfragen aussehen, die darauf zugreifen. Dieser Prozess, den man Denormalisierung nennt, steht im Gegensatz zu den fünf Normalformen, die das relationale Modell charakterisieren, und verstößt gegen Codds 12 Regeln für relationale Daten. Du erinnerst dich daran, dass du in dieser Welt lebst und nicht in einer theoretischen Wolke, und tust dann, was du tun musst, damit die Anwendung wieder akzeptabel reagiert, auch wenn sie nicht mehr "rein" ist.

Die 12 Regeln von Codd

Codd stellte eine Liste von 12 Regeln auf (eigentlich sind es 13, nummeriert von 0 bis 12), mit denen er seine Definition des relationalen Modells formalisierte, um auf die Abweichung der kommerziellen Datenbanken von seinen ursprünglichen Konzepten zu reagieren. Codd stellte seine Regeln in zwei Artikeln in der Zeitschrift CompuWorld im Oktober 1985 vor und formalisierte sie in der zweiten Ausgabe seines Buches The Relational Model for Database Management, das inzwischen vergriffen ist. Obwohl Codds Regeln ein ideales System darstellen, das von kommerziellen Datenbanken in der Regel nur teilweise umgesetzt wurde, haben sie bis heute einen entscheidenden Einfluss auf die relationale Datenmodellierung ausgeübt.

Das kommt dir wahrscheinlich bekannt vor. Im Web können Ingenieure zu Recht darüber nachdenken, ob diese Situation nicht mit Henry Fords Behauptung vergleichbar ist, dass man ab einem bestimmten Punkt nicht nur ein schnelleres Pferd haben will. Und sie haben einige beeindruckende, interessante Arbeit geleistet.

Wir müssen uns also zunächst bewusst machen, dass das relationale Modell einfach nur ein Modell ist. Das heißt, es soll eine nützliche Art sein, die Welt zu betrachten, die auf bestimmte Probleme anwendbar ist. Es erhebt nicht den Anspruch, erschöpfend zu sein und alle anderen Möglichkeiten der Datendarstellung auszuschließen, so dass kein Raum mehr für Alternativen bleibt. Wenn man die Geschichte betrachtet, war das Modell von Dr. Codd zu seiner Zeit ein ziemlicher Umbruch. Es war neu, mit einem seltsamen neuen Vokabular und Begriffen wie Tupel - vertrauteWörter, die auf eine neue und andere Weise verwendet wurden. Das relationale Modell wurde misstrauisch beäugt und hatte zweifelsohne seine vehementen Verfechter. Sogar Dr. Codds eigener Arbeitgeber, IBM, war dagegen. Das Unternehmen hatte eine sehr lukrative Produktpalette rund um IMS und konnte es nicht gebrauchen, dass ein junger Emporkömmling seinen Kuchen stückelte.

Aber das relationale Modell genießt jetzt wohl den besten Platz im Haus in der Datenwelt. SQL wird weithin unterstützt und gut verstanden. Es wird in Einführungskursen an Universitäten gelehrt. Cloud-basierte Platform-as-a-Service (PaaS)-Anbieter wie Amazon Web Services, Google Cloud Platform, Microsoft Azure, Alibaba und Rackspace bieten den Zugriff auf relationale Datenbanken als Service an, einschließlich automatischer Überwachungs- und Wartungsfunktionen. Welche Datenbank du letztendlich verwendest, hängt oft von den Architekturstandards in deinem Unternehmen ab. Auch wenn es keine solchen Standards gibt, ist es ratsam, sich mit der Datenbankplattform vertraut zu machen, die in deinem Unternehmen bereits vorhanden ist. Deine Kollegen in der Entwicklung und in der Infrastruktur verfügen über ein beträchtliches, hart erarbeitetes Wissen.

Durch Osmose (oder Trägheit) hast du im Laufe der Jahre gelernt, dass eine relationale Datenbank eine Einheitslösung ist.

Die bessere Frage lautet also vielleicht nicht: "Was ist falsch an relationalen Datenbanken?", sondern eher: "Welches Problem hast du?"

Das heißt, du willst sicherstellen, dass deine Lösung zu dem Problem passt, das du hast. Es gibt bestimmte Probleme, die relationale Datenbanken sehr gut lösen. Aber die explosionsartige Ausbreitung des Internets und insbesondere der sozialen Netzwerke bedeutet eine entsprechende Explosion der Datenmenge, mit der du umgehen musst. Als Tim Berners-Lee Anfang der 1990er Jahre das erste Mal am Web arbeitete, diente es dem Austausch wissenschaftlicher Dokumente zwischen Doktoranden in einem Physiklabor. Heute ist das Web natürlich so allgegenwärtig, dass es von allen genutzt wird, von denselben Wissenschaftlern bis hin zu Legionen von Fünfjährigen, die Emoji über Kätzchen austauschen. Das bedeutet zum Teil, dass es enorme Datenmengen verarbeiten muss. Die Tatsache, dass es das tut, ist ein Denkmal für die geniale Architektur des Webs.

Aber als die traditionellen relationalen Datenbanken unter der Last zu leiden begannen, wurde klar, dass neue Lösungen benötigt wurden.

Ein kurzer Überblick über relationale Datenbanken

Obwohl du wahrscheinlich mit ihnen vertraut bist, wollen wir uns kurz mit einigen grundlegenden Konzepten der relationalen Datenbanken befassen. Auf dieser Grundlage können wir uns dann mit den neueren Erkenntnissen über die Kompromisse befassen, die mit verteilten Datensystemen verbunden sind, insbesondere mit sehr großen verteilten Datensystemen, wie sie im Web benötigt werden.

Es gibt viele Gründe dafür, dass die relationale Datenbank in den letzten vier Jahrzehnten so überwältigend populär geworden ist. Ein wichtiger Grund ist die Structured Query Language (SQL), die viele Funktionen bietet und eine einfache, deklarative Syntax verwendet. SQL wurde 1986 erstmals offiziell als Standard des American National Standards Institute (ANSI) verabschiedet. Seitdem wurde sie mehrfach überarbeitet und durch herstellerspezifische Syntax wie T-SQL von Microsoft und PL/SQL von Oracle erweitert, um zusätzliche implementierungsspezifische Funktionen zu bieten.

SQL ist aus einer Vielzahl von Gründen leistungsstark. Es ermöglicht dem Benutzer, komplexe Beziehungen zu den Daten darzustellen, indem er Anweisungen verwendet, die die Data Manipulation Language (DML) bilden, um Daten einzufügen, auszuwählen, zu aktualisieren, zu löschen, abzuschneiden und zusammenzuführen. Mithilfe von Funktionen, die auf der relationalen Algebra basieren, kannst du eine Vielzahl von Operationen durchführen, um z. B. einen Maximal- oder Minimalwert in einer Menge zu finden oder um Ergebnisse zu filtern und zu ordnen. SQL-Anweisungen unterstützen die Gruppierung von aggregierten Werten und die Ausführung von Zusammenfassungsfunktionen. SQL bietet die Möglichkeit, Schemastrukturen zur Laufzeit mit der Data Definition Language (DDL) direkt zu erstellen, zu ändern und zu löschen. Mit SQL kannst du außerdem Benutzern und Benutzergruppen mit der gleichen Syntax Rechte gewähren und entziehen.

SQL ist einfach zu benutzen. Die grundlegende Syntax kann schnell erlernt werden, und konzeptionell bieten SQL und RDBMS eine niedrige Einstiegshürde. Junge Entwickler/innen können sich schnell einarbeiten, und wie so oft in einer Branche, die von schnellen Veränderungen, knappen Fristen und explodierenden Budgets geprägt ist, kann die Benutzerfreundlichkeit sehr wichtig sein. Und nicht nur die Syntax ist einfach zu bedienen, es gibt auch viele robuste Tools mit intuitiven grafischen Oberflächen für die Anzeige und Arbeit mit deiner Datenbank.

Auch weil SQL ein Standard ist, kannst du dein RDBMS leicht in eine Vielzahl von Systemen integrieren. Alles, was du brauchst, ist ein Treiber für deine Anwendungssprache, und schon kannst du auf sehr portable Weise loslegen. Wenn du dich entscheidest, deine Anwendungssprache (oder deinen RDBMS-Anbieter) zu wechseln, kannst du das oft problemlos tun, vorausgesetzt, du hast dich nicht mit vielen proprietären Erweiterungen in die Ecke gedrängt .

Transaktionen, ACID-ity und Two-Phase Commit

Zusätzlich zu den bereits erwähnten Funktionen unterstützen RDBMS und SQL auch Transaktionen. Ein wesentliches Merkmal von Transaktionen ist, dass sie zunächst virtuell ausgeführt werden und es dem Programmierer ermöglichen, Änderungen, die während der Ausführung schief gelaufen sind, rückgängig zu machen (mit Rollback); wenn alles gut gelaufen ist, kann die Transaktion zuverlässig abgeschlossen werden. Wie Jim Gray es ausdrückt, ist eine Transaktion "eine Zustandsänderung", die die ACID-Eigenschaften besitzt (siehe "Das Transaktionskonzept: Vorzüge und Grenzen").

ACID ist ein Akronym für Atomic, Consistent, Isolated, Durable. Das sind die Maßstäbe, mit denen du beurteilen kannst, ob eine Transaktion ordnungsgemäß ausgeführt wurde und ob sie erfolgreich war:

Atomic
Atomic bedeutet "alles oder nichts"; das heißt, wenn eine Anweisung ausgeführt wird, muss jede Aktualisierung innerhalb der Transaktion erfolgreich sein, um als erfolgreich bezeichnet zu werden. Es gibt keinen teilweisen Fehlschlag, bei dem eine Aktualisierung erfolgreich war und eine andere damit verbundene Aktualisierung fehlschlug. Ein gängiges Beispiel dafür sind Geldüberweisungen an einem Geldautomaten: Die Überweisung erfordert eine Abbuchung von einem Konto und eine Gutschrift auf einem anderen Konto. Dieser Vorgang kann nicht aufgeteilt werden; beide müssen erfolgreich sein.
Konsistent
Konsistent bedeutet, dass die Daten von einem korrekten Zustand in einen anderen korrekten Zustand übergehen, ohne dass Leser verschiedene Werte sehen können, die zusammen keinen Sinn ergeben. Wenn eine Transaktion zum Beispiel versucht, einen Kunden und seine Bestellhistorie zu löschen, kann sie keine Bestellzeilen zurücklassen, die auf den Primärschlüssel des gelöschten Kunden verweisen; dies ist ein inkonsistenter Zustand, der zu Fehlern führen würde, wenn jemand versucht, diese Bestelldatensätze zu lesen.
Isoliert
Isoliert bedeutet, dass Transaktionen, die gleichzeitig ausgeführt werden, nicht miteinander verflochten werden; sie werden jeweils in ihrem eigenen Bereich ausgeführt. Das heißt, wenn zwei verschiedene Transaktionen versuchen, dieselben Daten zur gleichen Zeit zu ändern, muss eine von ihnen warten, bis die andere fertig ist.
Langlebig
Wenn eine Transaktion erfolgreich abgeschlossen wurde, gehen die Änderungen nicht verloren. Das bedeutet nicht, dass eine andere Transaktion nicht später dieselben Daten ändert; es bedeutet nur, dass die Schreiber sicher sein können, dass die Änderungen für die nächste Transaktion verfügbar sind, um sie bei Bedarf zu bearbeiten.

Die Debatte über die Unterstützung von Transaktionen ist schnell ein wunder Punkt in Gesprächen über nicht-relationale Datenspeicher, also lass uns kurz darauf zurückkommen, was das wirklich bedeutet. Oberflächlich betrachtet scheinen die ACID-Eigenschaften so offensichtlich wünschenswert zu sein, dass sie nicht einmal eine Diskussion wert sind. Vermutlich würde niemand, der eine Datenbank betreibt, auf die Idee kommen, dass Datenaktualisierungen nicht eine gewisse Zeit überdauern müssen; das ist ja gerade der Sinn von Aktualisierungen - dass sie für andere lesbar sind. Bei genauerer Betrachtung könntest du jedoch einen Weg finden, diese Eigenschaften ein wenig zu optimieren und zu kontrollieren. Es gibt, wie man so schön sagt, kein kostenloses Mittagessen im Internet, und wenn du siehst, wie du für Transaktionen bezahlst, fragst du dich vielleicht, ob es eine Alternative gibt.

Transaktionen werden unter hoher Last schwierig. Wenn du zum ersten Mal versuchst, eine relationale Datenbank horizontal zu skalieren und sie so zu einer verteilten Datenbank zu machen, musst du nun verteilte Transaktionen berücksichtigen, bei denen die Transaktion nicht nur in einer einzigen Tabelle oder einer einzigen Datenbank stattfindet, sondern über mehrere Systeme verteilt ist. Um die ACID-Eigenschaften von Transaktionen weiterhin zu gewährleisten, brauchst du jetzt einen Transaktionsmanager, der die Transaktionen über die verschiedenen Knoten hinweg verwaltet.

Um einen erfolgreichen Abschluss über mehrere Hosts hinweg zu ermöglichen, wird die Idee eines zweiphasigen Commits (manchmal auch als "2PC" bezeichnet) eingeführt. Der Two-Phase-Commit ist ein gängiger Algorithmus, um in verteilten Systemen einen Konsens zu erreichen. Er umfasst zwei Interaktionsphasen zwischen den Hosts, die Vorbereitungsphase und die Commit-Phase. Da der Two-Phase-Commit alle zugehörigen Ressourcen sperrt, ist er nur für Operationen geeignet, die sehr schnell abgeschlossen werden können. Auch wenn es oft der Fall ist, dass deine verteilten Operationen in weniger als einer Sekunde abgeschlossen werden können, ist das sicher nicht immer der Fall. Einige Anwendungsfälle erfordern die Koordination zwischen mehreren Hosts, die du vielleicht nicht selbst kontrollierst. Vorgänge, die mehrere verschiedene, aber zusammenhängende Aktivitäten koordinieren, können Stunden für die Aktualisierung benötigen.

Zweiphasige Commit-Blöcke, d.h. Kunden ("konkurrierende Verbraucher") müssen warten, bis eine vorherige Transaktion abgeschlossen ist, bevor sie auf die blockierte Ressource zugreifen können. Das Protokoll wartet auf die Antwort eines Knotens, auch wenn dieser bereits gestorben ist. Es ist möglich, das ewige Warten in diesem Fall zu vermeiden, denn es kann eine Zeitüberschreitung festgelegt werden, die es dem Knoten des Transaktionskoordinators ermöglicht, zu entscheiden, dass der Knoten nicht antworten wird und er die Transaktion abbrechen sollte. Eine Endlosschleife ist mit 2PC jedoch immer noch möglich, weil ein Knoten eine Nachricht an den Transaktionskoordinator senden kann, in der er sich damit einverstanden erklärt, dass der Koordinator die gesamte Transaktion bestätigt. Der Knoten wartet dann darauf, dass der Koordinator eine Commit-Antwort sendet (oder eine Rollback-Antwort, wenn z. B. ein anderer Knoten nicht committen kann); wenn der Koordinator in diesem Szenario ausfällt, kann der Knoten ewig warten .

Um diese Unzulänglichkeiten beim zweiphasigen Commit verteilter Transaktionen auszugleichen, hat die Datenbankwelt die Idee der Kompensation aufgegriffen. Kompensation, die oft in Webservices verwendet wird, bedeutet einfach ausgedrückt, dass die Operation sofort übertragen wird und dann, falls ein Fehler gemeldet wird, eine neue Operation aufgerufen wird, um den korrekten Zustand wiederherzustellen.

Es gibt ein paar grundlegende, bekannte Muster für kompensatorische Maßnahmen, die Architekten häufig als Alternative zum zweiphasigen Commit in Betracht ziehen müssen. Dazu gehören das Abschreiben der Transaktion, wenn sie fehlschlägt, die Entscheidung, fehlerhafte Transaktionen zu verwerfen und später auszugleichen. Eine andere Alternative ist, fehlgeschlagene Vorgänge nach einer Benachrichtigung später erneut zu versuchen. In einem Reservierungssystem oder einem Börsenticker wird dies wahrscheinlich nicht deinen Anforderungen entsprechen. Für andere Anwendungen, wie z. B. Abrechnungs- oder Ticketing-Anwendungen, kann dies akzeptabel sein.

Das Problem mit Two-Phase Commit

Gregor Hohpe, ein Google-Architekt, schrieb einen wunderbaren und oft zitierten Blogeintrag mit dem Titel "Starbucks Does Not Use Two-Phase Commit". Er zeigt in der Praxis, wie schwierig es ist, Two-Phase-Commit zu skalieren und hebt einige der Alternativen hervor, die hier erwähnt werden. Es ist eine einfache, unterhaltsame und aufschlussreiche Lektüre. Wenn du tiefer einsteigen willst, findest du in Martin Kleppmans umfassendem Buch Designing Data-Intensive Applications (O'Reilly) eine hervorragende und ausführliche Diskussion über Two-Phase Commit und andere Konsensalgorithmen.

Zu den Problemen, die 2PC für Anwendungsentwickler mit sich bringt, gehören der Verlust der Verfügbarkeit und höhere Latenzzeiten bei Teilausfällen. Keines dieser Probleme ist wünschenswert. Wenn du also das Glück hast, so erfolgreich zu sein, dass du deine Datenbank über eine einzelne Maschine hinaus skalieren musst, musst du herausfinden, wie du Transaktionen auf mehreren Maschinen abwickeln kannst, ohne dass die ACID-Eigenschaften verloren gehen. Egal, ob du 10, 100 oder 1.000 Datenbankrechner hast, die Transaktionen müssen immer noch so atomar sein, als würdest du auf einem einzigen Knoten arbeiten. Aber das ist jetzt eine viel, viel größere Pille, die du schlucken musst.

Schema

Ein oft gepriesenes Merkmal relationaler Datenbanksysteme sind die umfangreichen Schemata, die sie bieten. Du kannst deine Domänenobjekte in einem relationalen Modell darstellen. Eine ganze Branche hat sich um (teure) Tools wie den CA ERwin Data Modeler gebildet, um diese Aufgabe zu unterstützen. Um ein richtig normalisiertes Schema zu erstellen, bist du jedoch gezwungen, Tabellen zu erstellen, die es als Geschäftsobjekte in deinem Bereich nicht gibt. Ein Schema für eine Universitätsdatenbank könnte zum Beispiel eine Tabelle "Studierende" und eine Tabelle "Kurse" erfordern. Da es hier aber eine "Many-to-many"-Beziehung gibt (ein Student kann viele Kurse gleichzeitig belegen, und ein Kurs hat viele Studenten gleichzeitig), musst du eine Join-Tabelle erstellen. Das verschmutzt ein ursprüngliches Datenmodell, in dem du lieber nur Schüler und Kurse hättest. Außerdem bist du gezwungen, komplexere SQL-Anweisungen zu erstellen, um diese Tabellen miteinander zu verbinden. Die Join-Anweisungen wiederum können langsam sein.

In einem System von bescheidener Größe ist das kein großes Problem. Aber komplexe Abfragen und Mehrfach-Joins können sehr langsam werden, wenn du eine große Anzahl von Zeilen in vielen Tabellen zu verarbeiten hast.

Schließlich lassen sich nicht alle Schemata gut auf das relationale Modell übertragen. Ein Systemtyp, der in den letzten zehn Jahren immer beliebter geworden ist, ist das komplexe Ereignisverarbeitungssystem oder Stream Processing System, das Zustandsänderungen in einem sehr schnellen Strom darstellt. Oft ist es sinnvoll, Ereignisse zur Laufzeit mit anderen Ereignissen in Beziehung zu setzen, um daraus Schlussfolgerungen für die Entscheidungsfindung abzuleiten. Obwohl Ereignisströme in Form einer relationalen Datenbank dargestellt werden können, wie z. B. mit der KSQL von Apache Kafka, ist das oft ein unangenehmer Aufwand.

Wenn du ein Anwendungsentwickler bist, kennst du sicher die vielen objektrelationalen Mapping-Frameworks (ORM), die in den letzten Jahren entstanden sind, um die Schwierigkeiten bei der Abbildung von Anwendungsobjekten auf ein relationales Modell zu verringern. Auch hier kann ORM für kleine Systeme eine Erleichterung sein. Aber es bringt auch neue Probleme mit sich, wie z. B. einen erhöhten Speicherbedarf, und verschmutzt den Anwendungscode oft mit zunehmend unhandlichem Mapping-Code. Hier ist ein Beispiel für eine Java-Methode, die Hibernate nutzt, um den SQL-Code zu "entlasten":

@CollectionOfElements
@JoinTable(name="store_description",
  joinColumns = @JoinColumn(name="store_code"))
@MapKey(columns={@Column(name="for_store",length=3)})
@Column(name="description")
private Map<String, String> getMap() {
  return this.map;
}
//... etc.

Ist es sicher, dass wir hier nichts anderes getan haben, als das Problem zu verschieben? Natürlich gibt es bei einigen Systemen, z. B. bei solchen, die viel mit dem Austausch von Dokumenten arbeiten, wie bei Diensten oder XML-basierten Anwendungen, nicht immer klare Zuordnungen zu einer relationalen Datenbank. Das verschlimmert das Problem noch.

Sharding und Shared-Nothing-Architektur

Wenn du es nicht aufteilen kannst, kannst du es nicht skalieren.

Randy Shoup, Ausgezeichneter Architekt, eBay

Eine andere Möglichkeit, eine relationale Datenbank zu skalieren, ist die Einführung von Sharding in deiner Architektur. Dies hat sich bei großen Websites wie eBay, die täglich Milliarden von SQL-Anfragen verarbeiten, und bei anderen modernen Webanwendungen bewährt. Die Idee dahinter ist, dass du die Daten aufteilst, so dass du, anstatt alle Daten auf einem einzigen Server zu hosten oder alle Daten auf allen Servern in einem Cluster zu replizieren, Teile der Daten horizontal aufteilst und sie jeweils separat hostest.

Nehmen wir zum Beispiel eine große Kundentabelle in einer relationalen Datenbank. Die am wenigsten störende Maßnahme (zumindest für die Programmierer) ist die vertikale Skalierung durch mehr CPUs, mehr Speicher und schnellere Festplatten. Wenn du aber weiterhin erfolgreich bist und mehr Kunden hinzufügst, musst du irgendwann (vielleicht im zweistelligen Millionenbereich) darüber nachdenken, wie du weitere Maschinen hinzufügen kannst. Wenn du das tust, kopierst du dann einfach die Daten, so dass sie auf allen Rechnern vorhanden sind? Oder teilst du stattdessen die Kundentabelle so auf, dass jede Datenbank nur einen Teil der Datensätze hat, wobei die Reihenfolge erhalten bleibt? Wenn die Kunden dann Abfragen durchführen, belasten sie nur den Rechner, der den gesuchten Datensatz hat, und nicht die anderen Rechner.

Es scheint klar zu sein, dass du für das Sharding einen guten Schlüssel finden musst, nach dem du deine Datensätze ordnen kannst. Du könntest zum Beispiel deine Kundendatensätze auf 26 Rechner aufteilen, einen für jeden Buchstaben des Alphabets, und auf jedem Rechner nur die Datensätze der Kunden speichern, deren Nachnamen mit diesem Buchstaben beginnen. Wahrscheinlich ist das aber keine gute Strategie - es gibt wahrscheinlich nicht viele Nachnamen, die mit "Q" oder "Z" beginnen, so dass diese Maschinen untätig bleiben, während die Maschinen mit "J", "M" und "S" überlaufen. Du könntest die Daten auch nach etwas Numerischem aufteilen, z. B. nach der Telefonnummer, dem Datum "Mitglied seit" oder dem Namen des Bundeslandes des Kunden. Es kommt ganz darauf an, wie deine Daten verteilt werden sollen.

Es gibt drei grundlegende Strategien zur Bestimmung der Scherbenstruktur:

Merkmalsbasierte Scherbenbildung oder funktionale Segmentierung

Diesen Ansatz verfolgte Randy Shoup, Distinguished Architect bei eBay, der 2006 dabei half, die Architektur der Website so weit zu entwickeln, dass sie viele Milliarden Abfragen pro Tag unterstützen kann. Bei dieser Strategie werden die Daten nicht durch die Aufteilung von Datensätzen in einer einzigen Tabelle aufgeteilt (wie in dem bereits erwähnten Kundenbeispiel), sondern durch die Aufteilung von Merkmalen, die sich nur wenig überschneiden, in separate Datenbanken. Bei eBay zum Beispiel befinden sich die Nutzer in einem Shard und die zu verkaufenden Artikel in einem anderen. Bei diesem Ansatz kommt es darauf an, deine Domäne zu verstehen, damit du die Daten sauber segmentieren kannst.

Schlüsselbasiertes Sharding

Bei diesem Ansatz findest du einen Schlüssel in deinen Daten, der sie gleichmäßig auf die Shards verteilt. Anstatt also einfach einen Buchstaben des Alphabets für jeden Server zu speichern, wie in dem (naiven und unsachgemäßen) früheren Beispiel, verwendest du einen Einweg-Hash für ein Schlüsseldatenelement und verteilst die Daten entsprechend dem Hash auf die Rechner. Bei dieser Strategie ist es üblich, zeitbasierte oder numerische Schlüssel für den Hash zu verwenden.

Nachschlagetabelle

Bei diesem Ansatz, der auch als verzeichnisbasiertes Sharding bekannt ist, fungiert einer der Knoten im Cluster als "Gelbe Seiten"-Verzeichnis und schaut nach, welcher Knoten die Daten hat, auf die du zugreifen willst. Das hat zwei offensichtliche Nachteile. Der erste ist, dass du jedes Mal, wenn du die Nachschlagetabelle als zusätzlichen Sprung durchlaufen musst, einen Leistungseinbruch erleidest. Zweitens wird die Nachschlagetabelle nicht nur zu einem Engpass, sondern auch zu einem Single Point of Failure.

Sharding kann je nach Strategie Konflikte minimieren und ermöglicht dir nicht nur eine horizontale Skalierung, sondern auch eine präzisere Skalierung, da du den Shards, die sie benötigen, Leistung hinzufügen kannst.

Sharding könnte man als eine Art Shared-Nothing-Architektur bezeichnen, die speziell für Datenbanken gilt. Bei einer Shared-Nothing-Architektur gibt es keinen zentralisierten (gemeinsamen) Status, sondern jeder Knoten in einem verteilten System ist unabhängig, so dass die Clients nicht um gemeinsame Ressourcen konkurrieren.

Die Shared-Nothing-Architektur wurde in jüngerer Zeit von Google populär gemacht, das Systeme wie seine Bigtable-Datenbank und seine MapReduce-Implementierung entwickelt hat, die keinen gemeinsamen Status haben und daher nahezu unbegrenzt skalieren können. Die Cassandra-Datenbank ist eine Shared-Nothing-Architektur, da sie keinen zentralen Controller und keine primären/sekundären Replikate kennt; alle ihre Knoten sind gleich.

Mehr über Shared-Nothing-Architektur

Der Begriff wurde erstmals von Michael Stonebraker von der University of California in Berkeley in seinem 1986 erschienenen Aufsatz "The Case for Shared Nothing" geprägt . Es sind nur ein paar Seiten. Wenn du einen Blick darauf wirfst, wirst du sehen, dass viele der Eigenschaften von Shared-Nothing-Architekturen für verteilte Daten, wie z. B. die einfache Hochverfügbarkeit und die Fähigkeit, auf eine sehr große Anzahl von Rechnern zu skalieren, genau das sind, was Cassandra auszeichnet.

Viele nicht-relationale Datenbanken bieten diese Funktion automatisch an, was sehr praktisch ist; das manuelle Erstellen und Verwalten eigener Daten-Shards ist ein schwieriges Unterfangen. MongoDB zum Beispiel, auf das wir später noch eingehen werden, bietet automatische Sharding-Funktionen, um Failover und Knotenausgleich zu verwalten. Es ist gut, Sharding in Bezug auf die Datenarchitektur im Allgemeinen und speziell in Bezug auf Cassandra zu verstehen. Cassandra verwendet einen ähnlichen Ansatz wie das schlüsselbasierte Sharding, um die Daten auf die Knoten zu verteilen, allerdings geschieht dies automatisch.

Web-Skala

Zusammenfassend lässt sich sagen, dass relationale Datenbanken sehr gut darin sind, bestimmte Probleme bei der Speicherung von Daten zu lösen, aber aufgrund ihres Schwerpunkts können sie auch ihre eigenen Probleme verursachen, wenn es an der Zeit ist, zu skalieren. Dann musst du oft einen Weg finden, deine Joins loszuwerden, was bedeutet, dass du die Daten denormalisieren musst, was wiederum bedeutet, dass du mehrere Kopien der Daten aufbewahren musst, was dein Design sowohl in der Datenbank als auch in deiner Anwendung erheblich stört. Außerdem musst du mit ziemlicher Sicherheit einen Weg finden, um verteilte Transaktionen zu umgehen, die schnell zu einem Engpass werden. Diese Ausgleichsmaßnahmen werden nur von den teuersten RDBMS direkt unterstützt. Und selbst wenn du eine so umfangreiche Prüfung schreiben kannst, musst du die Partitionierungsschlüssel so sorgfältig auswählen, dass du die Einschränkung nie ganz ignorieren kannst.

Was vielleicht noch wichtiger ist: Wenn du dir die Grenzen von RDBMS und die Strategien ansiehst, mit denen die Architekten ihre Skalierungsprobleme entschärft haben, zeichnet sich langsam ein Bild ab. Es ist ein Bild, das einige NoSQL-Lösungen weniger radikal und beängstigend erscheinen lässt, als du anfangs vielleicht dachtest, sondern eher wie ein natürlicher Ausdruck der Arbeit, die bereits für die Verwaltung sehr großer Datenbanken geleistet wurde.

Aufgrund einiger inhärenter Designentscheidungen in RDBMS sind diese nicht immer so einfach zu skalieren wie andere, neuere Möglichkeiten, die die Struktur des Webs berücksichtigen. Aber es ist nicht nur die Struktur des Webs, die du berücksichtigen musst, sondern auch sein phänomenales Wachstum, denn je mehr Daten zur Verfügung stehen, desto mehr brauchst du Architekturen, die es deinem Unternehmen ermöglichen, diese Daten nahezu in Echtzeit für die Entscheidungsfindung zu nutzen und deinen Kunden neue und leistungsfähigere Funktionen und Möglichkeiten anzubieten.

Datenskala, damals und heute

Es wird behauptet, dass der englische Dichter John Milton im 17. Jahrhundert jedes veröffentlichte Buch auf der Welt gelesen haben soll, obwohl es schwer zu überprüfen ist. Milton kannte viele Sprachen (zum Zeitpunkt seines Todes lernte er sogar Navajo), und wenn man bedenkt, dass die Gesamtzahl der veröffentlichten Bücher zu dieser Zeit in die Tausende ging, wäre das durchaus möglich gewesen. Die Größe der weltweiten Datenspeicher ist seitdem etwas gewachsen.

Mit dem rasanten Wachstum des Internets gibt es eine große Vielfalt an Daten, die gespeichert, verarbeitet und abgefragt werden müssen, und eine gewisse Vielfalt an Unternehmen, die diese Daten nutzen. Dabei geht es nicht nur um Kundendaten bei bekannten Einzelhändlern oder Lieferanten und nicht nur um digitale Videoinhalte, sondern auch um die notwendige Umstellung auf digitales Fernsehen und das explosive Wachstum von E-Mail, Messaging, Mobiltelefonen, RFID, Voice Over IP (VoIP) und dem Internet der Dinge (IoT). Unternehmen, die Inhalte bereitstellen, und die um sie herum aufgebauten Mehrwertgeschäfte von Drittanbietern, benötigen sehr skalierbare Datenlösungen. Bedenke auch, dass ein typischer Anwendungsentwickler oder Datenbankadministrator daran gewöhnt ist, relationale Datenbanken als das Zentrum des Universums zu betrachten. Du bist vielleicht überrascht, wenn du erfährst, dass in Unternehmen etwa 80 % der Daten unstrukturiert sind.

Der Aufstieg von NoSQL

Das jüngste Interesse an nicht-relationalen Datenbanken spiegelt das wachsende Bedürfnis der Softwareentwickler nach webbasierten Datenlösungen wider. Der Begriff "NoSQL" wurde um 2009 herum populär und diente als Kurzform für diese Datenbanken. Der Begriff war in der Vergangenheit Gegenstand vieler Diskussionen, aber es hat sich ein Konsens herausgebildet, dass er sich auf nicht-relationale Datenbanken bezieht, die "nicht nur SQL"-Semantiken unterstützen.

Verschiedene Experten haben versucht, diese Datenbanken in ein paar grobe Kategorien einzuteilen; schauen wir uns ein paar der gängigsten an:

Schlüssel-Wert-Läden

In einem Key-Value-Store sind die Datenelemente Schlüssel, die eine Reihe von Attributen haben. Alle Daten, die für einen Schlüssel relevant sind, werden zusammen mit dem Schlüssel gespeichert; Daten werden häufig dupliziert. Beliebte Key-Value-Stores sind Amazons Dynamo DB, Riak und Voldemort. Außerdem fungieren viele beliebte Caching-Technologien als Key-Value-Stores, darunter Oracle Coherence, Redis und Memcached.

Säulenläden

In einem Spaltenspeicher, der auch als spaltenübergreifender Speicher oder spaltenorientierter Speicher bezeichnet wird, werden die Daten spaltenweise und nicht zeilenweise gespeichert. In einem Spaltenspeicher können zum Beispiel alle Kundenadressen zusammen gespeichert werden, sodass sie mit einer einzigen Abfrage abgerufen werden können. Beliebte Spaltenspeicher sind z. B. Apache Hadoop's HBase, Apache Kudu und Apache Druid.

Dokumentenspeicher

Die grundlegende Einheit der Speicherung in einer Dokumentendatenbank ist das vollständige Dokument, das oft in einem Format wie JSON, XML oder YAML gespeichert wird. Beliebte Dokumentenspeicher sind MongoDB, CouchDB und verschiedene öffentliche Cloud-Angebote.

Graph-Datenbanken

Graphdatenbanken stellen Daten als einen Graphen dar - ein Netzwerk aus Knoten und Kanten, die die Knoten verbinden. Sowohl Knoten als auch Kanten können Eigenschaften haben. Da sie den Beziehungen einen hohen Stellenwert einräumen, haben sich Graphdatenbanken wie Neo4j, JanusGraph und DataStax Graph beim Aufbau von sozialen Netzwerken und semantischen Webanwendungen bewährt.

Objektdatenbanken

Objektdatenbanken speichern Daten nicht in Form von Beziehungen, Spalten und Zeilen, sondern in Form von Objekten, wie sie in der objektorientierten Programmierung verstanden werden. Das macht es einfach, diese Datenbanken von objektorientierten Anwendungen aus zu nutzen. Mit Objektdatenbanken wie db4o und InterSystems Caché kannst du auf Techniken wie Stored Procedures und objektrelationale Mapping (ORM) Tools verzichten. Die am weitesten verbreitete Objektdatenbank ist Amazon Web Services' Simple Storage Service (S3).

XML-Datenbanken

XML-Datenbanken sind eine besondere Form von Dokumentendatenbanken, die speziell für die Arbeit mit in der eXtensible Markup Language (XML) beschriebenen Daten optimiert sind. Zu den so genannten "XML-nativen" Datenbanken gehören BaseX und eXist.

Multimodell-Datenbanken

Datenbanken, die mehr als einen dieser Stile unterstützen, werden immer beliebter. Diese "Multimodell"-Datenbanken basieren auf einer primären Datenbank (meist ein relationaler, Key-Value- oder Spaltenspeicher) und stellen zusätzliche Modelle als APIs auf dieser Datenbank zur Verfügung. Beispiele hierfür sind Microsoft Azure Cosmos DB, die Dokumenten-, Wide-List- und Graphen-APIs auf einen Key-Value-Speicher aufbaut, und DataStax Enterprise, das eine Graphen-API auf das Wide-List-Modell von Cassandra aufbaut. Multimodell-Datenbanken werden oft für ihre Fähigkeit angepriesen, einen Ansatz zu unterstützen, der als polyglotte Persistenz bekannt ist und bei dem verschiedene Microservices oder Komponenten einer Anwendung mit Daten interagieren können, die mehr als eines der hier beschriebenen Modelle verwenden. Ein Beispiel für polyglotte Persistenz wird in Kapitel 7 behandelt.

Mehr über NoSQL-Datenbanken erfahren

Eine umfassende Liste von NoSQL-Datenbanken findest du auf der NoSQL-Website. Die Seite DB-Engines bietet außerdem eine Rangliste der beliebtesten Datenbanken nach Typ, die monatlich aktualisiert wird.

Die Ziele und Funktionen dieser Datenbanken sind sehr unterschiedlich, aber sie haben eine Reihe von gemeinsamen Merkmalen. Das offensichtlichste dieser Merkmale wird durch den Namen NoSQL impliziert - diese Datenbanken unterstützen Datenmodelle, Datendefinitionssprachen (DDLs) und Schnittstellen, die über das Standard-SQL der gängigen relationalen Datenbanken hinausgehen. Darüber hinaus sind diese Datenbanken in der Regel verteilte Systeme ohne zentrale Steuerung. Sie legen Wert auf horizontale Skalierbarkeit und hohe Verfügbarkeit, in manchen Fällen auf Kosten von starker Konsistenz und ACID-Semantik. Sie neigen dazu, eine schnelle Entwicklung und Bereitstellung zu unterstützen. Sie verfolgen einen flexiblen Ansatz bei der Schemadefinition, in manchen Fällen muss kein Schema im Voraus definiert werden. Sie unterstützen Big Data und analytische Anwendungen.

In den letzten zehn Jahren gab es eine große Anzahl von Open-Source- und kommerziellen Angeboten im NoSQL-Bereich. Die Akzeptanz und Qualität dieser Angebote war sehr unterschiedlich, aber in den oben genannten Kategorien haben sich führende Anbieter herauskristallisiert, und viele von ihnen haben sich zu ausgereiften Technologien mit einer großen Installationsbasis und kommerziellem Support entwickelt. Wir freuen uns, berichten zu können, dass Cassandra eine dieser Technologien ist, auf die wir im nächsten Kapitel näher eingehen werden.

Zusammenfassung

Das relationale Modell hat der Softwareindustrie in den letzten vier Jahrzehnten gute Dienste geleistet, aber das Maß an Verfügbarkeit und Skalierbarkeit, das für moderne Anwendungen erforderlich ist, hat die traditionelle relationale Datenbanktechnologie an ihre Grenzen gebracht.

Die Absicht dieses Buches ist es nicht, dich mit cleveren Argumenten davon zu überzeugen, eine nicht-relationale Datenbank wie Apache Cassandra einzusetzen. Wir wollen dir nur zeigen, was Cassandra kann und wie es funktioniert, damit du eine fundierte Entscheidung treffen und mit der praktischen Arbeit beginnen kannst, wenn du sie für sinnvoll hältst.

Die ultimative Frage lautet also vielleicht nicht : "Was ist falsch an relationalen Datenbanken?", sondern eher: "Was würde ich mit Daten machen, wenn sie kein Problem wären?" In einer Welt, die jetzt im Web arbeitet und in die Zukunft blickt, könnte Apache Cassandra ein Teil der Antwort sein.

Get Cassandra: The Definitive Guide, (Revised) Third Edition, 3. now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.