NoSQL Teil 2: Key-Value Stores am Beispiel von Redis
Einfach und schnell
Im ersten Teil unserer NoSQL Blogartikel-Reihe, haben wir aufgezeigt, dass das WWW neue Problembereiche im Bereich Datenspeicherung und Datenabfrage geschaffen hat, welche nicht optimal von relationalen Datenbankmanagementsystemen (DBMS) gelöst werden können. Unter dem Begriff NoSQL werden all jene DBMS gefasst, welche genau einen Problemtypus in einem besseren Kosten/Nutzen Verhältnis lösen, als die klassischen relationalen DBMS. Dabei kann der Bereich der NoSQL-DBMS in verschiedenste Kategorien aufgeteilt werden. Im Folgenden wollen wir einen der einfachsten (aus API-Sicht), aber auch zugleich flexibelsten NoSQL-Datenbanktypen vorstellen: Den Key-Value Store.
Key-Value Stores gehören zu der Kategorie der aggregat-orientierten Datenbanken. Aggregate sind nach Martin Fowler “[...] Zusammenfassungen von Entitäten und Wertobjekten und deren Assoziationen untereinander zu einer gemeinsamen transaktionalen Einheit. Aggregate definieren genau eine Entität als einzigen Zugriff auf das gesamte Aggregat.” Ein Beispiel-Aggregat wäre z.B. eine Bestellung (als „Aggregation Root“) mit all seinen Bestellpositionen, bei der der Zugriff auf die Positionen ausschließlich über die Bestellung erlaubt wäre. In Key-Value Stores wird einem eindeutigen Schlüssel - meist ein simpler String - ein Wert zugeordnet. Dieser Wert kann sowohl ein primitiver Datentyp (wie z.B. ein String, Integer etc.) oder bspw. auch ein serialisiertes Objekt sein, das ein Aggregat repräsentieren kann. Abfragen und Löschoperationen können nur über den eindeutigen Schlüssel des Wertes geschehen. Eine Bearbeitung ist nicht möglich. Daraus ergeben sich im einfachsten Fall folgende drei Operation welche die Schnittstelle eines Key-Value Stores beschreiben:
- put(key, value)
- get(key)
- remove(key)
Anwendungsfälle
Zum Einsatz kommen Key-Value Stores überall dort, wo zum einen das Datenmodell den Zugriff und Speicherung nur nach Schlüssel zulässt, vor allem aber dort wo die Performanz bei einer großen Anzahl an Objekten eine kritische Rolle spielt. Hohe Lese-/Schreibe-Raten werden durch die einfache Indizierung (nur nach dem Primärschlüssel) und der einfachen, meist linearen, Skalierung erreicht. Anwendungsbeispiele, bei denen die Geschwindigkeit kritisch ist, sind Session-Daten, Warenkörbe in E-Commerce-Applikationen oder auch einfache Caching-Szenarien. Key-Value Stores sind dagegen nicht für Anwendungsszenarien geeignet, bei denen komplexe Abfragen möglich sein sollen, z.B. über Aggregate hinweg oder bei komplexen Datenmodellen. In diesen Fällen lohnt es sich, einen Blick auf die übrigen NoSQL Typen zu werfen, die wir in kommenden Blogartikeln beschreiben werden.
Beispielimplementierungen für Key-Value Stores sind Redis, Riak, Memcached oder auch der Software-as-a-service-Dienst Amazon Dynamo DB. Sie unterscheiden sich vor allem in folgenden Fragen:
- Werden über einfache Key-Values hinweg weitere Datenstrukturen unterstützt (z.B. Listen oder Sets)?
- Werden die Daten volatil, z.B. im RAM, oder persistent auf der Festplatte vorgehalten?
- Wird über Sharding, Master-Slave oder Master-Master skaliert?
- Bietet die Implementierung strenge Konsistenz oder nur eventuelle?
Riak z.B. gehört zu den komplexeren persistenten Key-Value Stores, bei dem die gewünschte Konsistenz pro Abfrage konfigurierbar ist. Neben Keys und Values, sind noch Buckets im Datenmodell verfügbar, sowie eine Skalierung über mehrere Nodes ohne einem dedizierten Master. Memcached im Vergleich dazu ist recht simpel: Es hält die Key-Value Paare im RAM vor und skaliert mittels Sharding.
Redis als Key-Value Store
Im Folgenden wollen wir anhand von Redis die Funktionalität eines sehr verbreiteten und auch von Zweitag bei einer Vielzahl von Projekten eingesetzten Key-Value Stores genauer betrachten.
Redis ist ein in C implementierter in-memory Key-Value Store. Neben Key-Value-Paaren bietet Redis erweiterte Datenstrukturen wie Listen, Hashes, Sets sowie Bitmaps an. Mittels Konfiguration ist es möglich, die zunächst nur volatil im RAM vorgehalten Daten regelmäßig auf die Festplatte zu speichern, um Datenverlust zu verhindern. So kann Redis sowohl im Caching als auch in Fällen, wo die dauerhafte Persistenz von Daten gefordert ist, eingesetzt werden. Wie die meisten Key-Value Stores bietet Redis eine sehr hohe Lese-/Schreib-Rate. Zusätzlich bringt Redis von Haus aus eine eigene Publish/Subscribe-Implementierung mit, welche zur Implementierung von Messaging-Systemen in verteilten Architekturen genutzt werden kann.
Im folgenden Beispiel nutzen wir das Ruby-Gem redis-rb um einige Funktionalität von Redis vorzustellen:
Dies sind nur einige der Funktionalitäten von Redis. Eine ausführliche Dokumentation ist auf http://redis.io zu finden.
Bei Zweitag werden Key-Value Stores regelmäßig in Projekten eingesetzt. Viele der oben genannten Implementierung haben einen hohen Reifegrad und werden von der Open-Source-Community kontinuierlich weiterentwickelt. Wir schätzen die Einfachheit, mit der Entwickler Herausforderungen, die auf das Key-Value Modell passen, meistern können, sowie die vorhandene Flexibilität um auf sich schnell ändernde externe Anforderung reagieren zu können. Für unsere Kunden bieten Key-Value Stores das optimale Kosten-Nutzen-Verhältnis, wenn es um die Performanz und Skalierung von bestimmten Daten in IT-Projekten geht.