This article is part of a series: Jump to series overview

Grundsätzlich ist der Key-Value-Store oder die Key-Value-Datenbank eine sehr einfache Speicherform, wie man sie bereits vom Konfigurationsdateien kennt, denn auch bei diesen referenziert ein Schlüssel auf einen Wert. Jedoch erweitern einige moderne Key-Value-Stores das Konzept der Konfigurationsdateien, indem sie nicht nur Strings, sondern auch andere Datentypen, wie z.B. Integer, Booleans, Sets / Listen oder sogar Objekte / Dictionaries als Wert speichern können.

Lesen und Schreiben: Nur über den Schlüssel

Der größte Vorteil von Key-Value-Datenbanken ist ihre meist enorme Geschwindigkeit. So erreicht z.B. redis Laufzeiten von O(1) für Lese- und Schreiboperationen. Dafür muss man allerdings auch auf komplexe Suchalgorithmen oder Indizes verzichten, der Zugriff auf einen Datensatz erfolgt lediglich über dessen Schlüssel.

Auch wenn der Einsatzzweck für Key-Value Stores zuerst einmal sehr grundsätzlich erscheint, da man immer auf den Schlüssel zugreifen muss, gibt es doch verschiedene Implementierungen zu komplexeren Problemen mit solchen Datenbanksystemen. Redis selbst beispielsweise hat einen twitter-clone mit ihrer Datenbank geschrieben, es gibt viele Tracking-Tools für Werbung und auch mehrere Speichersysteme zu Natural-Language-Processing-Analysen.

Dazu wird der Schlüssel so erweitert, dass er neben einem statischen String auch die ID eines Objekts enthält und die Information damit eindeutig abbildet. Eine Benutzerverwaltung sähe damit beispielsweise folgendermaßen aus:

INCR global:nextUserId => 1000
SET uid:1000:username antirez
SET uid:1000:password p1pp0

Der Pfeil deutet an, welchen Wert der Eintrag mit dem Schlüssel global:nextUserId enthält. Der Doppelpunkt dient dabei als Trennzeichen für bestimmte Namespaces, man könnte aber genausogut auch ein Slash oder einen Unterstrich verwenden. Ein neuer Benutzer wird dann angelegt, indem man an den Namespace uid die ID und einen Namen für die gespeicherte Informationsart anhängt. Vor dem Anlegen des nächsten Benutzers würde man global:nextUserId wieder erhöhen und dann die Schlüssel uid:1001:username und uid:1001:password erstellen.

Interne Relationen zwischen Objekten gibt es bei Key-Value-Datenbanken nicht, sämtliche Relationen müssen vom Entwickler in seinem eigenen Programm aufgelöst werden. So führt redis bei seinem twitter-clone beispielsweise eine Liste für followers ein:

uid:1000:followers => Set of uids of all the followers users

Diese enthält IDs von Benutzern, welche dem Benutzer mit der ID 1000 folgen. Um dies allerdings zu Namen aufzulösen, muss der Entwickler in seinem Programm alle Einträge uid:[nummer]:username auslesen.

Da es in Key-Value-Datenbanken außerdem keine Möglichkeit gibt, nach einem Wert zu suchen und die zugehörige ID auszulesen, muss für jede umgekehrte Beziehung ebenfalls ein Eintrag angelegt werden. Nehmen wir also an, man möchte die URL der Benutzerseite nicht mit der ID, sondern mit dem Namen schmücken: In solch einem Fall hat man beim Aufruf der Seite nur den Benutzernamen antirez, braucht aber zum Auslesen von uid:1000:description die ID 1000. Deshalb muss im System für solch einen Fall noch ein neuer Eintrag angelegt werden:

username:antirez:uid 1000

Einfache Partitionierung und Replikation

Die einfache Verteilung von Daten auf mehrere Server ist eine weitere Stärke von Key-Value-Datenbanken. Da auf jeden Eintrag nur über den Schlüssel zugegriffen werden kann, ist eine Verteilung des kompleten Datensatzes auf mehrere kleinere Server kein Problem. Eine zuvor festgelegte Hash-Funktion berechnet aus dem Namen des Schlüssels, welcher Server für die Verwaltung des Eintrags zuständig ist. Auf diesem Server wird der Eintrag dann abgelegt oder gesucht, es müssen nicht erst alle Server befragt werden.

Dazu bieten Key-Value-Stores auch Replikation zur Ausfallsicherung, welche je nach System etwas anders umgesetzt wird: Während Redis auf ein Master-Slave-Modell setzt, bei dem jedem Master mehrere Slaves zugeordnet werden können, setzt Project Voldemort auf einen Hash-Ring, bei dem alle Instanzen gleichberechtigt sind und keiner als Slave bezeichnet wird.

Fazit

Key-Value-Datenbanken eignen sich für besonders einfache Systeme, bei denen möglichst nur einseitige Beziehungen bestehen. Hat man zu viele wechselseitigen Verbindungen, welche man von beiden Seiten aus erreichen muss (z.B. ID => Username, Username => ID), wird das System schnell unübersichtlich und man muss sich ständig um die Verwaltung und das Löschen von mehreren Einträgen kümmern.

Wenn man diesen Nachteil allerdings bei der eigenen Projektkomplexität in Kauf nehmen kann, wird man mit einer extrem hohen Geschwindigkeit und einfachen Verteilung der Last auf mehrere Server belohnt. Diese ist jedoch auch nötig, da es das Ziel von Key-Value-Datenbanken ist, möglichst alle Einträge neben der Sicherung auf der Festplatte auch gleich im RAM vorzuhalten (bei Redis ist dies sogar Pflicht).

Je nach eigenen Wünschen muss man sich bei Key-Value-Stores insbesondere auch noch einmal mit dem Spezialfunktionen der verschiedenen Systeme auseinandersetzen. Da Key-Value-Stores grundsätzlich sehr einfache Systeme darstellen, haben sich starke Spezialisierungen herausgebildet. Während MemcacheDB noch die Reinform ist und lediglich Strings speichern kann, bietet Redis auch interne Operationen auf Listen und Sets (z.B. die Schnittmenge ermitteln) und Lord Voldemort geht mit der Unterstützung von JSON-Mapping bereits stark in Richtung einer dokumentenorientierten Datenbank.

global:nextUserI
I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.