Auswahl der richtigen NoSQL-Datenbank

Relationale Datenbanken dominierten lange die Softwareindustrie und sind mit Mechanismen wie Redundanz, Transaktionskontrolle und Standardschnittstellen sehr ausgereift. Sie konnten jedoch zunächst nur mäßig auf höhere Anforderungen an Skalierbarkeit und Performance reagieren. So kam ab Anfang 2010 verstärkt der Begriff NoSQL auf um neuartige Datenbanken zu beschreiben, die diesen Anforderungen besser genügten.

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 Anzahl an Daten erforderte zunehmend Cluster zur Datenspeicherung

Aggregierte Datenmodelle

Die relationale Datenbankmodellierung unterscheidet sich erheblich von den Arten von Datenstrukturen, die Anwendungsentwickler verwenden. Die Verwendung der von Entwickler*innen modellierten Datenstrukturen zur Lösung verschiedener Problemdomänen hat zu einer Abkehr von der relationalen Modellierung hin zu aggregierten Modellen geführt. Der größte Teil davon wird von Domain Driven Design angetrieben. Ein Aggregat ist eine Sammlung von Daten, mit denen wir als Einheit interagieren. Diese Aggregate bilden die Grenzen für ACID-Operationen, wobei die Schlüsselwerte, Dokumente und Column Family als Formen einer aggregatorientierten Datenbank angesehen werden können.

Aggregate erleichtern der Datenbank die Verwaltung der Datenspeicherung auf einem Cluster, da sich die Dateneinheit jetzt auf jedem Computer befinden kann. Aggregatorientierte Datenbanken funktionieren am besten, wenn die meisten Dateninteraktionen mit demselben Aggregat durchgeführt werden, z.B. wenn ein Profil mit allen Details abgerufen werden muss. Es ist besser, das Profil als Aggregationsobjekt zu speichern, und diese Aggregate zu verwenden, um Profildetails abzurufen.

Distributionsmodelle

Aggregatorientierte Datenbanken erleichtern die Verteilung von Daten, da der Verteilungsmechanismus nur das Aggregat verschieben muss und sich nicht um verwandte Daten kümmern muss, da alle verwandten Daten im Aggregat selbst enthalten sind. Dabei gibt es im Wesentlichen zwei Arten der Datenverteilung:

Sharding
Sharding verteilt unterschiedliche Daten auf mehrere Server, sodass jeder Server als einzige Quelle für eine Teilmenge von Daten fungiert.
Replikation

Replikation kopiert Daten über mehrere Server hinweg, sodass jedes Datum an mehreren Stellen gefunden werden kann. Die Replikation erfolgt in zwei Formen:

Die Master-Slave-Replikation macht einen Knoten zur autorisierenden Kopie, die Schreibvorgänge verarbeitet, während Slaves mit dem Master synchronisiert werden und möglicherweise Lesevorgänge verarbeiten.

Die Peer-to-Peer-Replikation ermöglicht Schreibvorgänge auf jeden Knoten. Die Knoten koordinieren sich um ihre Kopien der Daten zu synchronisieren.

Die Master-Slave-Replikation verringert zwar die Wahrscheinlichkeit von Aktualisierungskonflikten, aber die Peer-to-Peer-Replikation vermeidet das Schreiben allerVorgänge auf einen einzelnen Server, wodurch ein einzelner Fehlerpunkt vermieden wird. Ein System kann eine oder beide Techniken verwenden.

CAP-Theorem

In verteilten Systemen sind die folgenden drei Aspekte wichtig:

  • Konsistenz (engl.: Consistency)
  • Verfügbarkeit (engl.: Availability)
  • Ausfalltoleranz (engl.: Partition tolerance)

Eric Brewer hat das CAP-Theorem aufgestellt, das besagt, dass wir in jedem verteilten System nur zwei der drei Optionen auswählen können. Viele NoSQL-Datenbanken versuchen, Optionen bereitzustellen, bei denen ein Setup gewählt werden kann, die Datenbank entsprechend dein Anforderungen aufzusetzen. Wenn ihr beispielsweise Riak als verteilte Schlüssel-Werte-Datenbank betrachtet, gibt es im Wesentlichen die drei Variablen

r
Anzahl der Knoten, die auf eine Leseanforderung antworten sollen, bevor sie als erfolgreich angesehen wird
w
Anzahl der Knoten, die auf eine Schreibanforderung antworten sollen, bevor sie als erfolgreich angesehen wird
n
Anzahl der Knoten, auf denen 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 setzen. Damit haben wir jedoch den Cluster für Netzwerkpartitionen anfällig gemacht, da kein Schreibvorgang möglich ist wenn nur ein Knoten nicht antwortet. Wir können denselben Cluster für Schreib- oder Lesevorgänge hoch verfügbar machen, indem wir r = 1 und w = 1 setzen. Jetzt kann jedoch die Konsistenz beeinträchtigt werden, da einige Knoten möglicherweise nicht über die neueste Kopie der Daten verfügen. Das CAP-Theorem besagt, dass ihr, wenn ihr eine Netzwerkpartition erhaltet, die Verfügbarkeit von Daten gegen die Konsistenz von Daten abwägen müsst. Auch die Haltbarkeit kann gegen die Latenz abgewogen werden, insbesondere wenn ihr Fehler mit replizierten Daten überstehen möchtet.

Häufig benötigtet ihr bei relationalen Datenbanken kaum ein Verständnis für diese Anforderungen; nun werden diese wieder wichtig. So dürftet ihr gewöhnt gewesen sein, in relationalen Datenbanken Transaktionen zu verwenden. In NoSQL-Datenbanken stehen euch diese jedoch nicht mehr zur Verfügung und ihr müsst selbst überlegen, wie diese implementiert werden sollen. Muss das Schreiben transaktionssicher sein? Oder ist es auch akzeptabel, dass mal Daten verlorengehen? Schließlich kann manchmal auch ein externer Transaktionsmanager wie ZooKeeper hilfreich sein.

Verschiedene Arten von NoSQL-Datenbanken

NoSQL-Datenbanken können grob in vier Typen eingeteilt werden:

Schlüssel-Werte-Datenbanken

Schlüssel-Werte-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 nur speichert, ohne sich darum zu kümmern oder zu wissen, was sich darin befindet. Es liegt ausschließlich in der Verantwortung der Anwendung, zu verstehen, was gespeichert wurde. Da Schlüssel-Werte-Datenbanken immer den Primärschlüsselzugriff verwenden, weisen sie im Allgemeinen eine hohe Leistung auf und können leicht skaliert werden.

Einige der beliebtesten Schlüsselwertdatenbanken 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 sie sorgfältig auswählen da es große Unterschiede zwischen ihnen gibt. Während Riak z.B. die Daten persistent speichert ist dies bei Memcached üblicherweise nicht der Fall.

Dokumentendatenbanken

Diese Datenbanken speichern und rufen Dokumente ab, bei denen es sich um XML, JSON, BSON usw. handeln kann. Diese Dokumente sind hierarchische Baumdatenstrukturen, die aus Karten, Sammlungen und Skalarwerten bestehen können. Dokumentendatenbanken bieten umfangreiche Abfragesprachen und Konstrukte wie Datenbanken, Indizes usw., die einen einfacheren Ü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 sind hervorragend geeignet für Gruppen verwandter Daten, auf die häufig zusammen zugegriffen wird. Dies können beispielsweise alle Profilinformationen einer Person sein, nicht jedoch deren Aktivitäten.

Zwar kann jede Column Family mit der Zeile in einer RDBMS-Tabelle verglichen werden, in dem der Schlüssel die Zeile identifiziert und die Zeile aus mehreren Spalten besteht; aber in Column Family Stores müssen die verschiedene Zeilen nicht dieselben 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 einfach skalierbar beschrieben werden, da Schreibvorgänge über den Cluster verteilt sind. Der Cluster verfügt nicht über einen Masterknoten, sodass Lese- und Schreibvorgänge von jedem Knoten im Cluster ausgeführt werden können.

Graphdatenbank

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 euch einen Knoten als 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-Modelle
Labeled-Property-Graph
In einem Labeled-Property-Graph können sowohl Knoten als auch Kanten Eigenschaften (engl.: Properties) besitzen.
Resource Description Framework (RDF)
Im RDF werden Graphen mit Hilfe von Triplen repräsentiert. Ein Triple besteht aus drei Elementen in der Form Knoten-Kante-Knoten Subjekt --Prädikat-> Objekt, die als Ressourcen in Form einer weltweit eindeutigen URI oder als anonyme Ressource definiert werden. Um innerhalb einer Datenbank verschiedene Graphen verwalten zu können, werden diese als Quads gespeichert wobei ein Quad jedes Triple um eine Referenz 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 mit der Web Ontology Language auch vollständig entscheidbare Ontologien beschreiben.
Algorithmen

Wichtige Algorithmen zur Abfrage von Knoten und Kanten sind:

Breitensuche, Tiefensuche
Breitensuche (engl.: breadth-first search, BFS) ist ein Verfahren zum Durchlaufen der Knoten eines Graphen verwendet. Im Gegensatz zur Tiefensuche (engl.: depth-first search, DFS) werden zunächst alle Knoten beschritten, die vom Ausgangsknoten direkt erreichbar sind. Erst danach werden Folgeknoten beschritten.
Kürzester Pfad
Pfad zwischen zwei unterschiedlichen Knoten eines Graphen, welcher minimale Länge bezüglich einer Kantengewichtsfunktion hat.
Eigenvektor
In der linearen Algebra ein vom Nullvektor verschiedener Vektor, dessen Richtung durch die Abbildung nicht verändert wird. Ein Eigenvektor wird also nur skaliert und der Skalierungsfaktor wird als Eigenwert der Abbildung bezeichnet.
Abfragesprachen
Blueprints
eine Java-API für Eigenschaftsgraphen, die zusammen mit verschiedenen Graphdatenbanken verwendet werden kann.
Cypher
eine Abfragesprache entwickelt von Neo4j.
GraphQL
eine SQL-artige Abfragesprache
Gremlin
eine Open-Source-Graphen-Programmiersprache, die mit verschiedenen Graphdatenbanken (Neo4j, OrientDB) genutzt 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 meist nur für spezifische Bedingungen, z.B. für die Beziehungen zwischen Menschen. Das Hinzufügen weiterer Beziehungsarten ist dann normalerweise mit vielen Schemaänderungen verbunden.

In Graphdatenbanken ist das Durchlaufen der Verknüpfungen oder Beziehungen sehr schnell, da die Beziehung zwischen Knoten nicht erst zur Abfragezeit berechnet werden muss.

Einige der beliebtesten Graphdatenbanken sind

Neo4j
Home | GitHub | Docs
InfiniteGraph
Home

Auswahl der NoSQL-Datenbank

Allen NoSQL-Datenbanken ist gemein, 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 im implizierten Schema tolerieren, sodass sie keine Downtime zur Migration benötigen; sie sind daher vor allem bei Systemen, die 24/7 zur Verfügung stehen sollen, sehr beliebt.

Wie wählen wir nun aber unter so vielen NoSQL-Datenbanken die richtige aus? Im Folgenden können wir Ihnen nur einige allgemeine Kriterien nennen:

Schlüssel-Werte-Datenbanken
sind im Allgemeinen nützlich zum Speichern von Sessions, Benutzerprofilen und Einstellungen. Wenn jedoch Beziehungen zwischen den gespeicherten Daten abgefragt oder mehrere Schlüssel gleichzeitig bearbeitet werden sollen, würden wir Schlüsselwertdatenbanken vermeiden.
Dokumentendatenbanken
sind im Allgemeinen nützlich für Content-Management-Systeme und E-Commerce-Anwendungen. Wir würden die Verwendung von Dokumentdatenbanken jedoch vermeiden, wenn komplexe Transaktionen benötigt odermehrere Vorgänge oder Abfragen für unterschiedliche Aggregatstrukturen gestellt werden sollen.
Column Family Stores
sind im Allgemeinen nützlich für Content-Management-Systeme und für hohe Schreibvolumen wie z.B. bei der Protokollaggregation. Wir würden die Verwendung von Datenbanken für Column Family Stores vermeiden, die sich in der frühen Entwicklung befinden und deren Abfragemuster sich noch ändern können.
Graphdatenbanken
eignen sich sehr gut für Problembereiche, in denen wir Daten wie soziale Netzwerke, Geodaten, Routing-Informationen sowie Empfehlungssystem miteinander verbunden werden sollen.

Fazit

Der Aufstieg der NoSQL-Datenbanken führte nicht zum Niedergang der relationalen Datenbanken. Sie können gut nebeneinander existieren. Häufig werden unterschiedliche Datenspeichertechnologien verwendet um die Daten passend zu ihrer Struktur und erforderlichen Abfrage zu speichern.