Die Auswahl der richtigen NoSQL-Datenbank

Veit Schiele

22. April 2021

14–16 Minuten

../_images/choosing-the-right-nosql-database.png

Relationale Datenbanken haben die Softwareindustrie lange Zeit dominiert und sind mit Mechanismen wie Redundanz, Transaktionskontrolle und Standardschnittstellen sehr ausgereift. Auf höhere Anforderungen an Skalierbarkeit und Leistung konnten sie jedoch zunächst nur mäßig reagieren. Daher wurde ab Anfang 2010 zunehmend der Begriff NoSQL verwendet, um neue Arten von Datenbanken zu beschreiben, die diesen Anforderungen besser gerecht werden.

NoSQL-Datenbanken sollten die folgenden Probleme lösen:

  • Überbrücken der internen Datenstruktur der Anwendung und der relationalen Datenstruktur der Datenbank.

  • Abkehr von der Integration verschiedenster Datenstrukturen in ein einheitliches Datenmodell.

  • Die wachsende Datenmenge erforderte zunehmend Cluster zur Datenspeicherung

Aggregierte Datenmodelle

Die Modellierung relationaler Datenbanken unterscheidet sich stark von den Datenstrukturen, die in Anwendungen verwendet werden. Die Verwendung von Datenstrukturen, die modelliert werden, um verschiedene Problemdomänen zu lösen, hat zu einer Abkehr von der relationalen Modellierung hin zu aggregierten Modellen geführt. Dies ist größtenteils durch Domain Driven Design inspiriert. Ein Aggregat ist eine Sammlung von Daten, mit denen wir als Einheit interagieren. Diese Aggregate bilden die Grenzen für ACID-Operationen, wobei Key Values, Documents und Column Family als Formen einer aggregatororientierten Datenbank angesehen werden können.

Aggregate erleichtern der Datenbank die Verwaltung der Datenspeicherung in einem Cluster, da sich die Dateneinheit nun auf jedem beliebigen Computer befinden kann. Aggregator-orientierte Datenbanken funktionieren am besten, wenn die meisten Dateninteraktionen mit demselben Aggregat durchgeführt werden, z. B. wenn ein Profil mit all seinen Details abgerufen werden muss. Es ist besser, das Profil als Aggregationsobjekt zu speichern und diese Aggregate zum Abrufen von Profildetails zu verwenden.

Verteilungsmodelle

Aggregator-orientierte Datenbanken erleichtern die Verteilung von Daten, da der Verteilungsmechanismus nur das Aggregat verschieben muss und sich nicht um die zugehörigen Daten kümmern muss, da alle zugehörigen Daten im Aggregat selbst enthalten sind. Es gibt zwei Hauptarten der Datenverteilung:

Sharding

Beim Sharding werden verschiedene Daten auf mehrere Server verteilt, so dass jeder Server als eine einzige Quelle für eine Teilmenge von Daten fungiert.

Replikation

Bei der Replikation werden Daten über mehrere Server hinweg kopiert, so dass dieselben Daten an mehreren Orten zu finden sind. Es gibt zwei Formen der Replikation:

Bei der Master-Slave-Replikation ist ein Knoten die maßgebliche Kopie und verarbeitet Schreibvorgänge, während die Slaves mit dem Master synchronisiert werden und Lesevorgänge verarbeiten können.

Bei der Peer-to-Peer-Replikation sind Schreibvorgänge an jedem Knoten möglich. Die Knoten koordinieren sich, um ihre Kopien der Daten zu synchronisieren.

Die Master-Slave-Replikation verringert die Wahrscheinlichkeit von Aktualisierungskonflikten, aber die Peer-to-Peer-Replikation vermeidet, dass alle Operationen auf einen einzigen Server geschrieben werden und so einen Single Point of Failure. Ein System kann eine oder beide Techniken verwenden.

CAP-Theorem

In verteilten Systemen sind die folgenden drei Aspekte wichtig:

  • Konsistenz, englisch Consistency

  • Verfügbarkeit, englisch Availability

  • Partitionstoleranz, englisch Partition tolerance

Eric Brewer hat das CAP-Theorem aufgestellt, das besagt, dass wir in jedem verteilten System nur zwei der drei Optionen wählen können. Viele NoSQL-Datenbanken versuchen, Optionen anzubieten, bei denen ein Setup gewählt werden kann, um die Datenbank entsprechend den eigenen Anforderungen einzurichten. Wenn ihr z. B. Riak als verteilte Key-Value-Datenbank betrachtet, gibt es im Wesentlichen die drei Variablen

r

Anzahl der Knoten, die auf eine Leseanfrage antworten müssen, bevor sie als erfolgreich angesehen wird

w

Anzahl der Knoten, die auf eine Schreibanfrage antworten müssen, bevor diese als erfolgreich angesehen wird

n

Anzahl der Knoten, auf die die Daten repliziert werden, auch Replikationsfaktor genannt

In einem Riak-Cluster mit 5 Knoten können wir die Werte für r, w und n so anpassen, dass das System sehr konsistent ist, indem wir r = 5 und w = 5 einstellen. Dadurch wird der Cluster jedoch anfällig für Netzwerkpartitionen, da kein Schreiben möglich ist, wenn nur ein Knoten nicht antwortet. Wir können denselben Cluster für Schreib- oder Lesevorgänge hochverfügbar machen, indem wir r = 1 und w = 1 setzen. Jetzt kann jedoch die Konsistenz beeinträchtigt werden, da einige Knoten möglicherweise nicht die neueste Kopie der Daten haben. Das CAP-Theorem besagt, dass man bei einer Netzwerkpartition die Verfügbarkeit der Daten gegen die Konsistenz der Daten abwägen muss. Die Dauerhaftigkeit kann auch gegen die Latenzzeit abgewogen werden, insbesondere wenn man Ausfälle mit replizierten Daten überleben will.

Bei relationalen Datenbanken brauchte man oft wenig Verständnis für diese Anforderungen; jetzt werden sie wieder wichtig. So wart ihr es vielleicht gewohnt, in relationalen Datenbanken Transaktionen zu verwenden. In NoSQL-Datenbanken stehen euch diese jedoch nicht mehr zur Verfügung, und ihr müsst euch überlegen, wie sie implementiert werden sollen. Muss das Schreiben transaktionssicher sein? Oder ist es akzeptabel, dass Daten von Zeit zu Zeit verloren gehen? Schließlich kann manchmal ein externer Transaktionsmanager wie ZooKeeper hilfreich sein.

Verschiedene Typen von NoSQL-Datenbanken

NoSQL-Datenbanken lassen sich grob in vier Typen unterteilen:

Key-value databases

Schlüssel-Wert-Datenbanken sind aus API-Sicht die einfachsten NoSQL-Datenspeicher. Der Client kann entweder den Wert für den Schlüssel abrufen, einen Wert für einen Schlüssel eingeben oder einen Schlüssel aus dem Datenspeicher löschen. Der Wert ist ein BLOB, den der Datenspeicher einfach speichert, ohne sich darum zu kümmern oder zu wissen, was sich darin befindet. Es liegt allein in der Verantwortung der Anwendung, zu verstehen, was gespeichert wurde. Da Key-Value-Datenbanken immer auf Primärschlüssel zugreifen, sind sie im Allgemeinen sehr leistungsfähig und lassen sich leicht skalieren.

Einige der beliebtesten Key-Value-Datenbanken sind

Riak KV

Home | GitHub | Docs

Redis

Home | GitHub | Docs

Memcached

Home | GitHub | Docs

Berkeley DB

Home | GitHub | Docs

Upscaledb

Home | GitHub | C API Docs

Ihr müsst diese sorgfältig auswählen, da es große Unterschiede zwischen ihnen gibt. Während Riak beispielsweise Daten dauerhaft speichert, ist dies bei Memcached normalerweise nicht der Fall.

Dokumentendatenbanken

Diese Datenbanken speichern und rufen Dokumente ab, bei denen es sich um XML, JSON, BSON und so weiter handeln kann. Diese Dokumente sind hierarchische Baumdatenstrukturen, die aus Maps, Sammlungen und skalaren Werten bestehen können. Dokumentendatenbanken bieten reichhaltige Abfragesprachen und Konstrukte wie Datenbanken, Indizes usw., die einen leichteren Übergang von relationalen Datenbanken ermöglichen.

Einige der beliebtesten Dokumentendatenbanken sind

MongoDB

Home | GitHub | Docs

CouchDB

Home | GitHub | Docs

RavenDB

Home | GitHub | Docs

Elasticsearch

Home | GitHub | Docs

eXist

Home | GitHub | Docs

Column Family Stores

Diese Datenbanken speichern Daten in Column-Families als Zeilen, die einem Zeilenschlüssel zugeordnet sind. Sie eignen sich hervorragend für Gruppen verwandter Daten, auf die häufig gemeinsam zugegriffen wird. Das können z. B. alle Profilinformationen einer Person sein, aber nicht ihre Aktivitäten.

Während jede Column-Family mit einer Zeile in einer RDBMS-Tabelle verglichen werden kann, in der der Schlüssel die Zeile identifiziert und die Zeile aus mehreren Spalten besteht, müssen in Column-Family-Stores die verschiedenen Zeilen nicht die gleichen Spalten haben.

Einige der beliebtesten Column-Family-Stores sind

Cassandra

Home | GitHub | Docs

HBase

Home | GitHub | Docs

Hypertable

Home | GitHub | Docs

Cassandra kann als schnell und leicht skalierbar bezeichnet werden, da die Schreibvorgänge über den Cluster verteilt sind. Der Cluster hat keinen Masterknoten, so dass Lese- und Schreibvorgänge von jedem Knoten im Cluster durchgeführt werden können.

Graph-Datenbank

In Graphdatenbanken könnt ihr Entitäten mit bestimmten Eigenschaften und Beziehungen zwischen diesen Entitäten speichern. Entitäten werden auch als Knoten bezeichnet. Stellt eich einen Knoten als eine Instanz eines Objekts in einer Anwendung vor; Beziehungen können dann als Kanten bezeichnet werden, die ebenfalls Eigenschaften haben können und gerichtet sind.

Graph-Models

Labeled Property Graph

In einem Labelled-Property-Graph können sowohl Knoten als auch Kanten Eigenschaften haben.

Resource Description Framework (RDF)

In RDF werden Graphen durch Tripel dargestellt. Ein Tripel besteht aus drei Elementen in der Form Knoten-Kante-Knoten subject --predicate-> object, die als Ressourcen in Form einer global eindeutigen URI oder als anonyme Ressource definiert sind. Um unterschiedliche Graphen innerhalb einer Datenbank verwalten zu können, werden diese als Quads gespeichert, wobei ein Quad jedes Tripel um einen Verweis auf den zugehörigen Graphen erweitert. Aufbauend auf RDF wurde mit RDF Schema ein Vokabular entwickelt, um schwache Ontologien zu formalisieren und darüber hinaus vollständig entscheidbare Ontologien mit der Web Ontology Language zu beschreiben.

Algorithmen

Wichtige Algorithmen zur Abfrage von Knoten und Kanten sind:

Breadth-first search, depth-first search

BFS ist eine Methode zum Durchlaufen der Knoten eines Graphen. Im Gegensatz zur DFS werden zuerst alle Knoten durchlaufen, die vom Anfangsknoten aus direkt erreichbar sind. Erst dann werden die nachfolgenden Knoten durchlaufen.

Shortest path

Weg zwischen zwei verschiedenen Knoten eines Graphen, der unter Berücksichtigung einer Kantengewichtsfunktion die geringste Länge hat.

Eigenvektor

In der linearen Algebra ein vom Nullvektor verschiedener Vektor, dessen Richtung durch die Abbildung nicht verändert wird. Ein Eigenvektor wird daher nur skaliert, und der Skalierungsfaktor wird als Eigenwert der Abbildung bezeichnet.

Abfragesprachen

Blueprints

eine Java-API für Property-Graphs, die zusammen mit verschiedenen Graph-Datenbanken verwendet werden kann.

Cypher

eine von Neo4j entwickelte Abfragesprache.

GraphQL

eine SQL-ähnliche Abfragesprache

Gremlin

eine quelloffene Graph-Programmiersprache, die mit verschiedenen Graph-Datenbanken (Neo4j, OrientDB) verwendet werden kann.

SPARQL

vom W3C spezifizierte Abfragesprache für RDF-Datenmodelle.

Abgrenzung zu relationalen Datenbanken

Wenn wir Graphen in relationalen Datenbanken speichern wollen, geschieht dies in der Regel nur für bestimmte Bedingungen, z. B. für Beziehungen zwischen Personen. Das Hinzufügen weiterer Beziehungstypen erfordert dann in der Regel viele Schemaänderungen.

In Graphdatenbanken ist das Durchlaufen der Links oder Beziehungen sehr schnell, da die Beziehung zwischen den Knoten nicht zur Abfragezeit berechnet werden muss.

Einige der beliebtesten Graphdatenbanken sind

Neo4j

Home | GitHub | Docs

InfiniteGraph

Home

Auswahl der NoSQL-Datenbank

Was alle NoSQL-Datenbanken gemeinsam haben, ist, dass sie kein bestimmtes Schema erzwingen. Im Gegensatz zu relationalen Datenbanken mit starkem Schema müssen Schemaänderungen nicht zusammen mit dem Quellcode, der auf diese Änderungen zugreift, gespeichert werden. Schemalose Datenbanken können Änderungen des impliziten Schemas tolerieren, so dass sie keine Ausfallzeiten für die Migration erfordern; sie sind daher besonders beliebt für Systeme, die rund um die Uhr verfügbar sein müssen.

Doch wie wählt man die richtige NoSQL-Datenbank unter den vielen aus? Im Folgenden können wir euch nur einige allgemeine Kriterien nennen:

Key-Value-Datenbanken

eignen sich im Allgemeinen für die Speicherung von Sitzungen, Benutzerprofilen und Einstellungen. Sollen jedoch Beziehungen zwischen den gespeicherten Daten abgefragt oder mehrere Schlüssel gleichzeitig bearbeitet werden, würden wir Key-Value-Datenbanken vermeiden.

Dokumentdatenbanken

sind im Allgemeinen für Content-Management-Systeme und E-Commerce-Anwendungen nützlich. Von Dokumentendatenbanken ist jedoch abzuraten, wenn komplexe Transaktionen erforderlich sind oder mehrere Operationen oder Abfragen für verschiedene Aggregatstrukturen durchgeführt werden sollen.

Column-Family-Stores

sind in der Regel für Content-Management-Systeme und große Schreibvorgänge, wie z. B. die Aggregation von Protokollen, geeignet. Wir würden es vermeiden, Column-Family-Stores für Projekte zu verwenden, die sich noch in der Entwicklung befinden und deren Abfragemuster sich noch ändern können.

Graph-Datenbanken

eignen sich gut für Problembereiche, in denen wir Daten wie soziale Netzwerke, Geodaten, Routing-Informationen und andere Informationen miteinander verbinden müssen.

Schlussfolgerung

Der Aufstieg der NoSQL-Datenbanken hat nicht zum Untergang der relationalen Datenbanken geführt. Sie können gut koexistieren. Oft werden verschiedene Datenspeichertechnologien verwendet, um die Daten so zu speichern, dass sie Ihrer Struktur und der gewünschten Abfrage entsprechen.