Für Jahrzehnte waren relationale Datenbanken, die SQL als ihre Abfragesprache benutzt haben, als populärste Lösung etabliert. Diese Datenbanken wurden üblicherweise als Einzelinstanzserver betrieben. Mit dem Erfolg von weltweit aufrufbaren Webanwendungen änderten sich jedoch die Anforderungen an Datenbanken: Die Anzahl an gleichzeitigen Zugriffen sowie deren Frequenz erhöhte sich drastisch und auch die Datenmengen wuchsen immens. Das brachte auf Datenbankseite eine Reihe von Problemen mit sich: Anfragen und Datenmenge waren von einem einzelnen Server unter Umständen nicht mehr handhabbar, die Latenzen bei geographisch weit entfernten Aufrufen hoch, vertikale Skalierung teuer. Gerade die großen Internet-Firmen wie Amazon, Google und Facebook hatten diese Probleme.

Um diese zu adressieren, entwickelte Google BigTable und MapReduce, und Amazon entwickelte Dynamo (alle 2004), welche eine horizontale Skalierung datenhaltender Systeme ermöglichte. Da diese Systeme zuerst nur intern zur Verfügung standen und nur in wissenschaftlichen Artikeln beschrieben wurden, begannen andere Firmen ähnlicher Größe diese Ideen für ihre eigenen Datenbanken zu verwenden wie zum Beispiel 2008 Facebook mit Cassandra und 2009 LinkedIn mit Voldemort. Aber auch kleinere Firmen wie Basho (Riak in 2009), sowie einige Open Source Projekte (HBase in 2006) implementierten diese Konzepte. Bis heute dient das Datenmodel von BigTable (Wide Column Stores), die Abfrage über MapReduce, sowie die Techniken aus dem Dynamo Paper (Consistent Hashing, Vector Clocks, Quorums, Merkle Trees und Gossip-basierte Membership Protokolle) vielen Datenbanken als Inspiration. Zur gleichen Zeit (2005) begann Damien Katz mit seiner von Lotus Notes inspirierten Arbeit an CouchDB. 2009 startete zudem die Arbeit an MongoDB und Redis.

Alle diese Datenbanken versuchen die Skalierbarkeitsprobleme zu adressieren und lassen das relationale Model hinter sich. Zudem boten diese Systeme weniger Garantien wie serialisierbare Transaktionen und Linearisierbarkeit, sowie Features wie Joins, die Entwickler die mit Relationalen Datenbanksystemen arbeiten, gewohnt waren. Diese Eigenschaften machten diese Systeme für einige Probleme ungeeignet oder senkten die Produktivität der Entwickler. In der Folge wurden auf der einen Seite NoSQL Datenbanken um einen Teil dieser Features und Eigenschaften erweitert, wie z.B. Joins, und auf der anderen Seite Ideen der NoSQL Datenbanken in relationale Datenbanken integriert oder durch Zusatztools ermöglicht, wie z.B. Replikationsmechanismen in Postgres ab Version 9. Neben dieser Entwicklung entstanden aber auch neue Datenbanken, die versuchen, das Beste aus beiden Welten zu bieten. Unter NewSQL Datenbanken verstehen wir Datenbanken, die dem relationalen Model folgen und SQL als Abfragesprache nutzen, sowie Transaktionen bieten, dabei aber versuchen, die Skalierbarkeit von NoSQL Lösungen zu erreichen. Wir unterscheiden hier in zwei Kategorien: Die eine Kategorie sind Erweiterungen von etablierten SQL Datenbanken, die andere sind Neuentwicklungen.

Erweiterungen von existierenden SQL Datenbanken

Bei den Erweiterungen finden wir zum einen neue Storage Engines, und zum anderen Sharding Proxy. Letztere gibt es schon länger intern bei eBay, Google und Facebook: Jede Datenbankinstanz enthält nur einen Teil der Daten, der Proxy sorgt dafür, dass dies nach außen hin nicht sichtbar ist und die Client API erhalten bleibt. Die meisten der Erweiterungen sind eine Kombination aus beidem. Hier ein paar Beispiele:

NewSQL Datenbanken mit neuer Architektur

Andere Datenbanken wurden von Grund auf neu entwickelt. Ein Teil dieser Datenbanken sind Closed Source und werden vollständig vom jeweiligen Cloudanbieter gemanagt. Bei diesem als “Database as a Service” (DaaS) genannten Ansatz werden auch Provisioning, Patches und Backups übernommen. Die Angebote starten bei einigen hundert Euro pro Monat. Es stehen aber auch Open Source NewSQL Lösungen zur Auswahl. In diesem Artikel wollen wir folgende Datenbanken vorstellen:

Features von NewSQL Datenbanken

Replikation

Replikation bedeutet, dass die gleichen Datensets auf verschiedenen Knoten abgelegt werden. Replikation ist sowohl zum Erreichen einer hohen Verfügbarkeit als auch zum Schutz vor Datenverlust essenziell.

Daten können synchron oder asynchron repliziert werden. In einer synchronen Replikation wird eine Transaktion erst als durchgeführt angesehen, wenn alle Replikate diese bestätigt haben. Um dies zu ermöglichen, können Verfahren wie Two-Phase Commits eingesetzt werden. Das Hinzufügen von weiteren Replikaten senkt in diesem Fall die Schreibgeschwindigkeit, da der Schreibvorgang immer nur so schnell ist wie der langsamste Replikatserver plus die Netzwerkverbindung.

Synchrone Replikation
Synchrone Replikation

Um die Schreibgeschwindigkeit zu verbessern, wird asynchrone Replikation verwendet. Dabei wird die Transaktion als geschrieben angesehen, sobald ein Knoten diese Transaktion bestätigt hat. Es können jedoch Inkonsistenz wie Stale Reads oder auch Schreibkonflikte auftreten. Wenn diese Inkonsistenz über die Zeit aufgelöst werden kann, sprechen wir von Eventual Consistency.

Asynchrone Replikation
Asynchrone Replikation

Eine weitere Anforderung ist die Replikation zwischen verschiedenen Rechenzentren. Diese Verteilung schützt gegen den Ausfall eines Rechenzentrums und beschleunigt geografisch verteilten Zugriffe. Mit der Replikation zwischen Rechenzentren sollten wir sparsam umgehen, da die Verbindung zwischen Rechenzentren um ein Vielfaches langsamer ist als innerhalb eines Rechenzentrums. Daher gibt es die Anforderung, die Entscheidung für Replikation auf Datenbank, Tabellen oder Index Level zu treffen.

Die Replikation von CockroachDB verwendet den Consensus Algorithmus Raft. CockroachDB garantiert dabei Linearisierbarkeit. Folglich wird bei CockroachDB stets Konsistenz der Verfügbarkeit vorgezogen. In verteilten Systemen können die Uhren der verschiedenen Knoten auseinander laufen. CockroachDB kann die Linearisierbarkeit nur gewährleisten, wenn diese Abweichung in einem konfigurierbaren Rahmen bleibt. Mehr dazu gibt es hier.

In der Dokumentation von Aurora gibt es wenige Angaben zur Replikation sowie zur Konsistenz. Mit Aurora Global Database (nur für die MySQL Version verfügbar) ist es möglich, die Daten über mehrere AWS Regionen zu verteilen.

Cosmos DB stellt dem Benutzer bezüglich Datenreplikation fünf verschiedene Konsistenzmodelle zur Auswahl, von starker Konsistenz mit garantierter Linearisierbarkeit bis zu Eventual Consistency und überlässt es dem Anwender die optimale Variante für seine Anwendung zu wählen. Zum Einsatz kommen hier neben einem Consensus Protokoll unter anderem Conflict-free replicated data type.

Spanner verwendet den Consensus Algorithmus Paxos, der sich ähnlich verhält wie Raft, so dass hier die gleichen Eigenschaften wie bei CockroachDB gelten. Um auch nur minimale Abweichungen in den Zeiten zwischen den verschiedenen Datencentern zu vermeiden, verbaut Google dort Atomuhren und GPS Empfänger.

Sharding

Sharding beziehungsweise Partitioning verteilt Datenmengen, die nicht auf einen Knoten passen auf mehrere Knoten. Beim Sharding wählen wir einen Shard Key aus, welcher verwendet wird, um einen Datensatz einem Shard zuzuordnen. Jeder dieser Shards ist dann auf einer oftmals konfigurierbaren Anzahl von Knoten zu finden, der Shard wird also repliziert. Bei der Auswahl des Shard Keys kann entweder der (gehashte) Primary Key verwendet werden oder auf einen fachlichen Schlüssel zurückgegriffen werden.

Beim Sharding werden die Datensätze in Shards (grün, rot, weiß) aufgeteilt. Die Shards werden dann auf mehrere Knoten verteilt.
Beim Sharding werden die Datensätze in Shards (grün, rot, weiß) aufgeteilt. Die Shards werden dann auf mehrere Knoten verteilt.

Sharding bringt zwei Herausforderungen mit sich:

  1. Joins finden nicht immer alle benötigten Daten auf einem Knoten. Joins müssen dann von mehreren Knoten ausgeführt und dieser Vorgang koordiniert werden. Netzwerkkommunikation ist dabei unumgänglich, was den Vorgang verlangsamt.
  2. Abfragen, die sich auf mehr als den primären Schlüssel beziehen, können nicht von einem Knoten alleine beantwortet werden. Das betrifft auch sekundäre Indexe.

Diese beiden Herausforderungen sind der Grund, warum viele NoSQL Datenbanken (zumindest zu Anfang) weder Joins noch sekundäre Indexe angeboten haben. NewSQL Datenbanken hingegen bieten oftmals beides.

Bei Aurora gibt es keine Angaben zu Möglichkeiten für Sharding, Joins oder sekundären Indexen.

CockroachDB bietet auch die Möglichkeit, die Shards nur in bestimmten Geozonen zu halten. Somit werden beispielsweise die Benutzeraccounts in Australien nur in Australien gehalten, die in Europa nur in Europa. Das hat Vorteile bei der Geschwindigkeit für die Benutzer und beim Datenschutz. Joins sind auch mit Daten möglich, die auf mehrere Knoten verteilt wurden.

Cosmos DB teilt die Daten anhand eines vom Benutzer festgelegten Attributs - dem Partition Key - gleichmässig auf mehrere Shards auf. Alle Daten werden von Cosmos DB automatisch indiziert, Queries die sich nicht auf den Partition Key beziehen sind jedoch mit höheren Kosten verbunden.

Spanner führt ein dynamische Sharding der Daten durch. Je nach Last werden Tabellen horizontal gesplittet und Shards hinzugefügt oder bei Bedarf auf wieder gemergt. Spanner untertstützt sowohl sekundäre Indexe wie auch Joins.

Flexible Datenmodellierung

Eine relationale Datenbank hat ein festes Schema für jede Tabelle. Das bedeutet, dass jeder Datensatz die gleiche Struktur hat. Wollen wir also weitere Felder hinzufügen, Felder entfernen oder modifizieren, muss eine Migration durchgeführt werden. Die meisten NoSQL Datenbanken haben kein Schema. Theoretisch kann so jedes Element einer Collection andere Attribute haben. In der Praxis ist das meistens nicht so, da unsere Anwendung das Schema vorgibt. Die Datenbank ist also nicht schemalos, sondern hat ein implizites Schema. Eine weitere damit verwandte Möglichkeit ist das Nesting: Hier kann ein Feld beispielsweise eine Liste von Strings sein oder Objekte können in anderen Objekten verschachtelt sein.

Viele SQL und NewSQL Datenbanken können mit schemalosen und verschachtelten Daten umgehen. Meistens passiert das in der Form von einem Feld Typ “JSON”. Da in SQL eine Abfrage dieser Art nicht vorgesehen ist, muss es entsprechend erweitert werden. Das macht es weniger komfortabel als im Falle einer Abfragesprache, bei der dies von Anfang an vorgesehen war. Zudem muss die Datenbank Indexe innerhalb dieser JSON Felder unterstützen.

Aurora macht keine Angaben zu unterstützten Feldtypen.

CockroachDB unterstützt JSON als Feldtyp. Die Abfrage erfolgt in der selben Art wie in PostgreSQL über eine Erweiterung von SQL.

Cosmos DB unterstützt ein schemafreies Datenmodell und speichert Daten in JSON Datenstrukturen in Form von Dokumenten. Unterstützt werden die JSON Datentypen, Abfragen können mit SQL formuliert werden.

Spanner ist eine schemabasierte Datenbank und für jede Spalte einer Tabelle muss explizit ein Datentyp festgelegt werden. Es unterstützt einfache Datentypen wie INT64 und BOOL und komplexe wie ARRAY, für eindimensionale Arrays, und STRUCT, für komplexe Datenstrukturen. Es gibt jedoch keinen expliziten JSON Datentyp.

Operational Simplicity

Ein weiteres Feature vieler NewSQL Datenbanken ist, dass sie einfach zu betreiben sind. Traditionell ist es für ein Datenbanksetup mit hoher Verfügbarkeit erforderlich, dass wir:

  1. Explizit Knoten als Leader oder Follower deklarieren. Leader und Follower ersetzen die alten Begriffe Master und Slave.
  2. Einen Watchdog oder ähnliches aufsetzen, der bei Ausfall des Leaders die Wahl eines neuen Leaders anstößt.
  3. Falls ein Knoten nach einem Ausfall wieder erreichbar ist, den Knoten wieder auf den aktuellen Stand bringen, indem wir ihn als Follower konfigurieren.

In den meisten der NewSQL Datenbanken ist das nicht nötig. Wir fahren auf jedem Knoten eine Instanz der Datenbank hoch und konfigurieren nur, wo die anderen Knoten zu finden sind. Die Knoten organisieren sich dann selbst und befreien die Administratoren von den oben genannten Arbeitsschritten. Fügen wir weitere Knoten hinzu oder ändern die Replikationszahl, kümmert sich die Datenbank automatisch um das Umverteilen der Knoten, um unsere Anforderungen zu erfüllen (Automatic Rebalancing). Das erleichtert den Betrieb gerade in Cloud Umgebungen wie Kubernetes.

Im Falle der Database as a Service Anbieter wird der gesamte Betrieb übernommen. Aurora bietet mit Serverless die Möglichkeit, bei hohen Zugriffszahlen automatisch die Datenbank zu skalieren.

CockroachDB gibt es sowohl als DaaS Angebot als auch mit der Möglichkeit, den Betrieb selbst zu übernehmen. In letzterem Fall wird der Betrieb so wie in diesem Abschnitt beschrieben vereinfacht.

Conclusion

Fazit

NewSQL Datenbanken bieten eine interessante Option für global genutzte Anwendungen die auf gewohnte Konsistenzgarantien wie ACID konforme Transaktionen nicht verzichten wollen. Zu beachten sind jedoch die relativ hohen Kosten der DaaS Angebote und auch dass bei ihnen, insbesondere bei Aurora, die technische Umsetzung vieler Versprechen nicht vollständig offengelegt ist und deshalb auch von externen Experten nicht verifiziert werden kann. Alle NewSQL Datenbanken scheinen sich für Consistency vor Availability zu entscheiden. Folglich sind NewSQL Datenbanken kein Ersatz für typische High Availability Systeme. Sie sind auch keine Revolution, sondern eine logische Weiterentwicklung von SQL Datenbanken.

TAGS