Cookies
Diese Website verwendet Cookies und ähnliche Technologien für Analyse- und Marketingzwecke. Durch Auswahl von Akzeptieren stimmen Sie der Nutzung zu, alternativ können Sie die Nutzung auch ablehnen. Details zur Verwendung Ihrer Daten finden Sie in unseren Datenschutz­hinweisen, dort können Sie Ihre Einstellungen auch jederzeit anpassen.

Engineering

NoSQL Teil 2: Key-Value Stores am Beispiel von Redis
Minuten Lesezeit
Blog Post - NoSQL Teil 2: Key-Value Stores am Beispiel von Redis
Nicolas Dillmann

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:

require "redis"

client = Redis.new

# Basisfunktionen eines Key-Value Stores:
client.set("my_key", "my_value")
=> "OK"

client.get("my_key")
=> "my_value"

client.del("my_key")
=> 1

# Da Redis nur Strings als Value zulässt, müssen Objekte vor der Speicherung serialisiert
# und nach dem Lesen deserialisiert werden.

class User
  attr_accessor :name
end

user = User.new
user.name = "Nic"

client.set("User:1", Marshal.dump(user))
=> "OK"

user = Marshal.load client.get("User:1")
=> #

user.name
=> "Nic"

# Redis bietet auch die Implementierung von atomaren Countern an,
# welche gerade im Analytics-Bereich Anwendung finden können.

client.set("my_counter", 0)
=> "OK"

client.incr("my_counter")
=> 1

client.incrby("my_counter", 7)
=> 8

client.decr("my_counter")
=> 7

# Listen bieten sich u.a. bei der Implementierung von Queues an.

client.lpush "my_list", 1
=> 1

client.lpush "my_list", 2
=> 2

client.lrange "my_list",  0, -1
=> ["2", "1"]

client.rpop "my_list"
=> "1"

# Hashes

client.hset "my_hash", :foo, "bar"
=> true

client.hget "my_hash", :foo
=> "bar"

client.hgetall "my_hash"
=> {"foo"=>"bar"}

# Sets sind eine ungeordnete Collection an Werten.

client.sadd "my_set", "Hello"
=> true
client.sadd "my_set", "World"
=> true

client.smembers "my_set"
=> ["Hello", "World"]

# Publish/Subscribe ist ein Muster, welches in verteilten Messaging-Systemen Anwendung findet.
# Mehrere Empfänger (Subscriber) können ein Thema (Channel) abonnieren und werden benachrichtigt,
# sobald ein Sender (Publisher) eine Nachricht zu diesem Thema veröffentlicht.

# Subscriber Client
client.subscribe(:channel_one) do |on|
  on.message do |channel, message|
    puts "#{message}"
  end
end

# Publisher Client
client.publish :channel_one, "Hello World"

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.

Ihr sucht den richtigen Partner für eure digitalen Vorhaben?

Lasst uns reden.