Link copied to the clipboard.
Wer sich nicht zwischen NoSQL und relationaler Datenbank entscheiden kann, könnte es mit einer NewSQL-Datenbank probieren. Bei dieser Gruppe von Lösungen handelt es sich ein wenig um die Quadratur des Kreises. Denn es wird versucht, die Vorzüge der ACID-Transaktionalität mit den Vorzügen der Verteilung zu kombinieren. Da wir derzeit noch nicht in der Lage sind, an den Grundfesten der uns bekannten Physik zu rütteln, funktionieren diese Konzepte auch nur in einem gewissen Rahmen. Verkürzt lässt sich sagen, dass auch für eine verteilte NewSQL-Datenbank das CAP-Theorem gilt. Die bekanntesten Vertreter dieser Kategorie sind vermutlich CockroachDB, YugabyteDB und Google Spanner. In letzter Zeit bewegt sich aber auch PostgreSQL immer weiter in diese Richtung. Wie aber machen die das mit der Konsistenz?
Um in einem verteilten System Konsistenz herzustellen, wird heute sehr häufig der RAFT-Algorithmus verwendet oder darauf aufgebaut (Details zu RAFT hier). Er basiert auf dem Ur-Consensus verteilter Systeme – auch bekannt als PAXOS. RAFT arbeitet dabei mit einem sogenannten Leader, das ist eine Instanz innerhalb eines Clusters, die Clientanfragen übergreifend koordiniert. Ein verteiltes System, das mit RAFT arbeitet, ist also niemals masterless. Masterless würde bedeuten, dass eine Anwendung/Client mit einem beliebigen Knoten eines Clusters kommunizieren kann.
RAFT versus masterless
Dadurch, dass allein der Leader eine Anfrage koordiniert, kann also auch sichergestellt werden, dass nicht zeitgleich zwei unterschiedliche Antworten das Cluster verlassen. Das System ist also erst einmal konsistent. Ein Leader kann darüber hinaus sicherstellen, dass eine Antwort immer wirklich den aktuellen Datenbestand wiedergibt, indem die vorhandenen Repliken aller Knoten anhand eines Zeitstempels abgeglichen werden. Ab einer bestimmten Systemgröße sollte einem an der Stelle jedoch etwas unwohl werden, denn in einem weltweit verteilten System mit vielen Instanzen, kann es durchaus etwas dauern, bis ein Datensatz durch einen Leader mit den Datenrepliken aller Follower-Instanzen abgeglichen wurde. Will man auf diese Weise hergestellte Konsistenz durchhalten, wird irgendwann die Performance leiden. Um die Performance bei größeren Installationen nicht in den Keller gehen zu lassen, muss also darauf verzichtet werden, alle Instanzen/Repliken eines Systems zu prüfen und alles über einen einzelnen Leader laufen zu lassen. Mit diesem Verzicht bleibt die Antwortzeit zwar schnell, könnte aber theoretisch auch nicht den aktuellen Datensatz zurückgeben.
Generell führt der RAFT-Algorithmus dazu, dass ein System nicht vollständig verfügbar sein kann. Das „A“ (Availability) des CAP-Theorems wird mit RAFT also niemals hundertprozentig erfüllt. Fällt nämlich ein Leader aus, beginnt das Cluster damit, einen neuen Leader aus der Mitte der Follower zu bestimmen. Bis dieser neue Leader gewählt wurde, ist das System auch nicht richtig verfügbar.
Eine Eigenschaft, mit der verteilte Datenbanksysteme gegenüber relationalen Lösungen punkten können, ist die horizontale Skalierbarkeit. Das bedeutet, mehr Leistung kann einfach mit mehr Serverinstanzen erzeugt werden. Bei einem zentralen/relationalen System ist es jedem klar, dass nur die Größe der einzelnen Server, nicht aber deren Anzahl für das Skalieren ausschlaggebend sein kann (vertikale Skalierung). Arbeitet eine verteilte Datenbank jedoch mit einem RAFT-Algorithmus, ist per Design ein Leader vorhanden, der sich genau nicht horizontal skalieren lässt.
Nehmen wir z. B. PostgreSQL. PostgreSQL ist eigentlich eine relationale Datenbank und wird auch als „vollständig ACID-konform“ (Quelle: The PostgreSQL Global Development Group) beschrieben. Gleichzeitig liest man in letzter Zeit immer wieder über Ansätze zur „horizontalen Skalierbarkeit“, was PostgreSQL vom Anspruch mehr in das Lager der NewSQL Stores verschiebt. Hier sollte aber differenziert werden, denn solange PostgreSQL mit einem RAFT-basierenden Verfahren zur Hochverfügbarkeit arbeitet (Patroni), wird eine lineare horizontale Skalierbarkeit nur ein Wunsch bleiben. Was PostgreSQL bietet, ist tatsächlich eine recht gute Möglichkeit der Lastverteilung von Rohdaten auf der Storage-Ebene, also architektonisch unterhalb der Kommunikationsschicht (des Leaders). Das allein nützt aber relativ wenig – z. B. bei massiven Lesezugriffen, die von außen auf einen einzelnen Leader einprasseln. Ergo: Systeme wie CockroachDB oder PostgreSQL, die mit RAFT arbeiten, können zwar gute Verteilungsmechanismen der Rohdaten anbieten, aber niemals vollständig horizontal skalieren. Das bleibt ausschließlich echten Peer-to-Peer-Systemen wie Cassandra vorbehalten, die vollständig auf einen Leader verzichten können.
Spark und Kafka – das sind keine Datenbanksysteme
Man liest und hört auch immer wieder, dass Tools wie Spark und Kafka in den Topf der Datenbanken geworfen werden. Ich halte das für grundlegend falsch, da der ursprüngliche Sinn dieser Systeme nichts mit der Funktion klassischer Datenbanken zu tun hat. Es mag zwar den einen oder anderen Fall geben, bei dem man tatsächlich auf eine Datenbank verzichten kann und es stattdessen möglich ist, auf Funktionen der genannten Systeme zurückzugreifen, jedoch handelt es sich dabei eher um einzelne Anwendungsfälle. Die Technologien generell als Datenbanken (im klassischen Sinne) zu missbrauchen, entspricht eher dem Paradigma, ein System solange zu verbiegen, bis es zum Anwendungsfall passt.
Spark ist ein Helferlein, um Daten im Hauptspeicher komplizierten und aufwendigen Berechnungen zu unterziehen. Es ist zwar auch möglich, die zu berechnenden Datenpakete so groß werden zu lassen, dass sie aus dem Onlinespeicher auf die Festplatte ausgelagert werden müssen, das widerspricht aber fundamental der eigentlichen Spark-Idee. Da im Idealfall nur In-Memory-Computing verwendet werden soll, legt die Lösungsarchitektur auch eher keinen Wert darauf, dass Daten bei einem Ausfall z. B. wiederherstellbar sind. So etwas kann man zwar machen, jedoch sind derartige Funktionen nie das Ziel von Spark gewesen. Von daher wird Spark auch im Normalfall nur neben einer anderen Datenbank oder auch File Systemen wie z. B. HDFS als Compute Engine verwendet. Schon aus Kostengründen ist es kaum sinnvoll, Daten massenhaft im Hauptspeicher eines Hosts zu halten. Das macht sonst nur SAP mit HANA – und ist dabei auch nicht ganz billig. Von daher sollte man mit Spark ausschließlich Daten temporär aus einer vorhandenen Persistenz nehmen, sie nach einem gewünschten Muster berechnen (zusammenführen, vergleichen, etc.) und das Ergebnis dann wieder in ein definiertes Ziel schreiben. Der Hauptspeicher wird dabei immer nur für Berechnungen verwendet und die Festplatte bleibt am besten „sauber“.
Mit Kafka verhält es sich im Grunde ähnlich, denn Kafka ist als Message Broker für Streaming Prozesse entwickelt worden. Was Kafka von anderen klassischen Messaging Systemen unterscheidet, ist, dass es die beiden Ansätze beim Messaging (Point to Point Queuing und Publish-Subscribe) durch sein Consumer-Group-Konzept vereinigt bzw. darüber hinaus die Möglichkeit bietet, Daten längerfristig zu speichern. Von daher lässt Kafka sich auch nicht gut in die Schublade klassischer Messaging-Systeme stecken, da es durchaus etwas mehr kann. Der Sinn ist im Kern aber immer noch, Daten von A nach B (oder C, D, …) zu transportieren, auch wenn die Kafka-Community das nicht gern hört und selbst lieber von einer multifunktionalen Streaming-Plattform spricht. Ein echtes Datenmodell, wie es eine relationale Datenbank mit sich führt, gibt es in diesem Sinne bei Kafka nicht. Daten werden zwar innerhalb vom Kafka für eine definierte Zeit persistiert, jedoch ist der Consumer (jemand, der Daten aus dem Kafka-Cluster erhält) selbst dafür verantwortlich, sich zu merken, welchen Datensatz er schon mit welcher ID gelesen hat. Man könnte an diesem Punkt natürlich ansetzen und sagen, dass der Consumer permanent mit Daten aus dem Kafka-Cluster arbeitet, also Daten on Demand (wie aus einer Datenbank) abruft. Dieses Konzept würde dann etwas dem Key-Value-Prinzip ähneln, jedoch wird der Key chronologisch von Kafka selbst erstellt und bietet keine Fachlichkeit.
Entscheidungen für oder gegen eine Technologie bedürfen also deutlich mehr Differenzierung als nur NoSQL versus Relationales Datenbanksystem. Wir haben gelernt, dass auch die Skalierbarkeit und ergänzende Technologien, die im Dunstkreis von Datenbankensystemen existieren, eine Rolle spielen und Stand heute niemand am CAP-Theorem vorbeikommt. Diese Erkenntnisse werden wir im letzten Teil dieser Serie noch einmal in einem anderen Kontext aufgreifen. Dabei soll es um die Kosten bzw. um die unterschiedlichen Wartungsmodelle der einzelnen Technologien gehen. Im Kern also um die Frage: Lizenz versus Open Source versus Subskription.