Strings, Hashes, Listen und Sets richtig einsetzen
Redis ist weit mehr als ein simpler Key-Value-Speicher fuer Strings. Wer die vier Kern-Datentypen Strings, Hashes, Listen und Sets kennt und ihr internes Memory-Encoding versteht, kann Datenmodelle bauen, die spuerbar weniger Speicher brauchen und deutlich schneller antworten als eine naive Implementierung mit nur einem Datentyp.
Inhaltsverzeichnis
- 1. Warum Redis-Datentypen mehr sind als Syntax
- 2. Strings: mehr als nur Zwischenspeicher
- 3. Hashes: strukturierte Objekte ohne Overhead
- 4. Listen: Queues und Aktivitaets-Feeds
- 5. Sets: Mitgliedschaft und Mengenoperationen
- 6. Memory-Encoding: listpack, intset und die Schwellenwerte
- 7. Die richtige Datenstruktur fuer den Use Case waehlen
- 8. Komplexitaetsklassen und Performance in der Praxis
- 9. Redis-Datentypen im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Redis-Datentypen mehr sind als Syntax
Wer Redis zum ersten Mal einsetzt, behandelt es oft wie einen glorifizierten Key-Value-Store: ein String als Wert, fertig. Das funktioniert, verschenkt aber den groessten Vorteil von Redis. Die eigentliche Staerke liegt in den Redis-Datentypen selbst: Strings, Hashes, Listen, Sets und Sorted Sets sind keine reinen Serialisierungsformate, sondern native Strukturen mit eigenen Commands, eigener Speicherorganisation und eigenen Komplexitaetsgarantien. Wer ein komplexes Objekt als JSON-String ablegt, muss es bei jeder kleinen Aenderung komplett lesen, deserialisieren, aendern und komplett zurueckschreiben.
Mit dem passenden Redis-Datentyp entfaellt dieser Umweg vollstaendig. Ein einzelnes Feld eines Hash laesst sich mit HSET aendern, ohne die restlichen Felder zu beruehren. Ein Element einer Liste laesst sich mit LPUSH anhaengen, ohne die komplette Liste neu zu serialisieren. Diese Operationen sind atomar, laufen serverseitig ab und sparen Netzwerk-Roundtrips. Wer die Redis-Datentypen bewusst waehlt, reduziert nicht nur den Speicherverbrauch, sondern auch die Anzahl der Befehle, die eine Anwendung pro Anfrage an Redis schickt.
Dieser Artikel geht die vier gebraeuchlichsten Redis-Datentypen im Detail durch: Strings, Hashes, Listen und Sets. Sorted Sets, HyperLogLog, Bitmaps und Streams sind eigene, spezialisiertere Strukturen und werden in separaten Beitraegen behandelt. Der Fokus hier liegt auf den Commands, dem internen Memory-Encoding und den Kriterien, nach denen man sich fuer eine Struktur entscheidet.
2. Strings: mehr als nur Zwischenspeicher
Der String ist der einfachste der Redis-Datentypen, aber bei weitem nicht der trivialste. Ein Redis-String kann bis zu 512 MB gross sein und ist binaersicher, das heisst er kann beliebige Bytes enthalten, nicht nur Text. Neben SET und GET bietet Redis atomare Zaehloperationen wie INCR, INCRBY und DECRBY, die ohne Race Condition funktionieren, selbst wenn hunderte Clients gleichzeitig zugreifen. Das macht Strings zur natuerlichen Wahl fuer Zaehler wie Seitenaufrufe, Rate-Limits oder Lagerbestaende.
Ein oft uebersehenes Feature ist die direkte Kombination von SET mit einer Time-to-Live per EX oder PX Option, wodurch ein Wert und sein Ablaufzeitpunkt in einem einzigen atomaren Befehl gesetzt werden. Das ist die Grundlage fuer Session-Speicher, Verifizierungscodes und temporaere Sperren. SETNX, beziehungsweise die modernere Form SET key value NX, setzt einen Wert nur, wenn der Schluessel noch nicht existiert, und ist damit die Basis fuer verteilte Locks. GETRANGE und SETRANGE erlauben Teiloperationen auf dem String, ohne den kompletten Wert zu uebertragen.
Fuer binaere Daten wie Bitmaps oder serialisierte Protobuf-Nachrichten ist der String-Typ ebenfalls die Grundlage, auch wenn spezialisierte Commands wie SETBIT darauf aufsetzen. In der Praxis ist der String der Redis-Datentyp mit dem hoechsten Nutzungsanteil, weil er fuer Caching-Szenarien mit einfachen Werten, Zaehlern und Flags ausreicht und keine zusaetzliche Modellierung erfordert.
# Strings: counters, TTL-based sessions, atomic locks
redis-cli SET pageviews:home 0
redis-cli INCR pageviews:home
redis-cli INCRBY pageviews:home 42
redis-cli SET session:abc123 "active" EX 3600
redis-cli TTL session:abc123
redis-cli SET lock:invoice:9001 "worker-1" NX EX 30
redis-cli GETRANGE session:abc123 0 2
redis-cli STRLEN session:abc123
redis-cli APPEND pageviews:home ":verified"
3. Hashes: strukturierte Objekte ohne Overhead
Ein Hash bildet ein Feld-Wert-Mapping innerhalb eines einzigen Schluessels ab und ist damit der natuerliche Redis-Datentyp fuer Objekte wie Benutzerprofile, Produktdaten oder Konfigurationssaetze. Statt fuer jedes Feld einen eigenen String-Schluessel wie user:1000:name und user:1000:email anzulegen, buendelt ein Hash alle Felder unter einem einzigen Top-Level-Schluessel user:1000. Das reduziert die Anzahl der Schluessel in der Keyspace-Tabelle drastisch, was direkt Speicher spart, weil jeder Top-Level-Schluessel in Redis einen eigenen Verwaltungsoverhead hat.
Mit HSET lassen sich einzelne oder mehrere Felder gleichzeitig setzen, mit HGET und HMGET gezielt einzelne oder mehrere Felder lesen, ohne das komplette Objekt zu uebertragen. HGETALL liefert das gesamte Objekt, sollte bei sehr grossen Hashes aber mit Bedacht eingesetzt werden, weil es eine O(n)-Operation ist. HINCRBY erlaubt atomare Zaehloperationen auf einzelnen Feldern, etwa fuer einen Login-Zaehler innerhalb eines Benutzerprofils, ohne dass ein separater Zaehler-Schluessel noetig ist.
Ein haeufiger Modellierungsfehler ist, ein komplettes Objekt als JSON-String in einem einzigen Redis-Key zu speichern, obwohl ein Hash die natuerlichere und effizientere Struktur waere. Der Unterschied zeigt sich bei Teil-Updates: Beim JSON-String muss die gesamte Zeichenkette gelesen, im Client geparst, ein Feld geaendert und die komplette Zeichenkette zurueckgeschrieben werden. Beim Hash genuegt ein einzelner HSET user:1000 last_login 1721742000 Aufruf, der serverseitig und atomar ablaeuft.
# Hashes: structured objects with partial field access
redis-cli HSET user:1000 name "Max Mustermann" email "max@example.com" plan "pro"
redis-cli HGET user:1000 email
redis-cli HMGET user:1000 name plan
redis-cli HGETALL user:1000
redis-cli HINCRBY user:1000 login_count 1
redis-cli HDEL user:1000 plan
redis-cli HEXISTS user:1000 email
redis-cli HLEN user:1000
4. Listen: Queues und Aktivitaets-Feeds
Redis-Listen sind doppelt verkettete Listen und damit der geeignete Redis-Datentyp fuer geordnete Sequenzen, bei denen Einfuegen und Entfernen an beiden Enden schnell sein muss. LPUSH fuegt Elemente am Kopf ein, RPUSH am Ende, beide Operationen laufen in konstanter Zeit O(1) ab, unabhaengig von der Listenlaenge. Diese Eigenschaft macht Listen zur Standardwahl fuer einfache Job-Queues: Producer haengen Aufgaben mit LPUSH an, Worker holen sie mit RPOP ab, das Ergebnis ist eine First-In-First-Out-Queue ohne zusaetzliche Infrastruktur.
Fuer blockierendes Warten auf neue Elemente bietet Redis BRPOP und BLPOP, die einen Client so lange blockieren, bis ein Element verfuegbar ist oder ein Timeout ablaeuft. Das ersetzt Polling-Schleifen, die unnoetig CPU und Netzwerkbandbreite verbrauchen wuerden. Fuer zuverlaessige Verarbeitung mit Wiederanlauf nach Worker-Absturz gibt es LMOVE, das ein Element atomar von einer Quell- in eine Ziel-Liste verschiebt, sodass ein Element waehrend der Verarbeitung in einer Processing-Liste sichtbar bleibt.
Neben Queues eignen sich Listen fuer begrenzte Aktivitaets-Feeds: Mit LPUSH gefolgt von LTRIM laesst sich eine Liste auf eine feste Laenge, etwa die letzten 100 Ereignisse, begrenzen. Fuer wahlfreien Zugriff auf ein einzelnes Element in der Mitte einer sehr langen Liste sind Listen aber die falsche Wahl, weil LINDEX in der Mitte der Liste linear traversieren muss und damit O(n) kostet, waehrend die Enden konstant erreichbar bleiben.
# Lists: job queues and bounded activity feeds
redis-cli LPUSH queue:emails "job:4821"
redis-cli LPUSH queue:emails "job:4822"
redis-cli RPOP queue:emails
redis-cli BRPOP queue:emails 5
redis-cli LMOVE queue:emails queue:emails:processing LEFT RIGHT
redis-cli LPUSH feed:user:1000 "liked photo:9981"
redis-cli LTRIM feed:user:1000 0 99
redis-cli LRANGE feed:user:1000 0 9
redis-cli LLEN queue:emails
5. Sets: Mitgliedschaft und Mengenoperationen
Ein Set speichert eine ungeordnete Sammlung eindeutiger Strings und beantwortet eine Frage besonders effizient: Ist dieses Element in der Menge enthalten? SISMEMBER laeuft in konstanter Zeit O(1), unabhaengig davon, wie viele Elemente das Set enthaelt. Damit ist der Set-Datentyp die richtige Wahl fuer Tag-Systeme, Berechtigungslisten oder das Deduplizieren von Ereignissen, etwa um zu pruefen, ob eine Benutzer-ID bereits an einer Aktion teilgenommen hat.
Die eigentliche Staerke von Sets zeigt sich bei Mengenoperationen: SINTER berechnet die Schnittmenge mehrerer Sets, SUNION die Vereinigung, SDIFF die Differenz. Diese Operationen laufen komplett serverseitig ab, ohne dass der Client Elemente einzeln laden und selbst vergleichen muss. Ein typisches Beispiel: Freunde-Empfehlungen ueber die Schnittmenge der Follower-Sets zweier Benutzer, oder das Finden von Produkten, die sowohl Tag A als auch Tag B tragen, per SINTERSTORE in einem neuen, wiederverwendbaren Set.
Fuer sehr grosse Mengen bietet SRANDMEMBER eine zufaellige Stichprobe ohne Entfernen des Elements, waehrend SPOP ein zufaelliges Element entfernt und zurueckgibt, praktisch fuer Lotterie-Mechaniken oder das Ziehen von Testkandidaten aus einem Pool. Wichtig zu verstehen: Sets garantieren keine Reihenfolge. Wer eine sortierte Mitgliederliste braucht, etwa nach Score oder Zeitstempel, sollte zum Sorted-Set-Datentyp wechseln, der in einem eigenen Beitrag behandelt wird.
# Sets: membership checks and server-side set operations
redis-cli SADD tags:article:501 "redis" "caching" "performance"
redis-cli SISMEMBER tags:article:501 "redis"
redis-cli SADD followers:userA "u1" "u2" "u3"
redis-cli SADD followers:userB "u2" "u3" "u4"
redis-cli SINTER followers:userA followers:userB
redis-cli SUNIONSTORE followers:combined followers:userA followers:userB
redis-cli SCARD followers:combined
redis-cli SPOP giveaway:candidates 3
6. Memory-Encoding: listpack, intset und die Schwellenwerte
Was viele Entwickler nicht wissen: Redis speichert kleine Instanzen jedes Datentyps intern in einer kompakteren Form, um Speicher zu sparen. Diese kompakte Form heisst listpack (in aelteren Versionen ziplist) und wird automatisch verwendet, solange ein Hash, eine Liste oder ein Set unter bestimmten Groessen-Schwellenwerten bleibt. Ein Set aus reinen Ganzzahlen wird sogar noch spezieller als intset kodiert, einem sortierten Array von Integers, das noch weniger Speicher braucht als ein listpack.
Die relevanten Konfigurationswerte heissen hash-max-listpack-entries, hash-max-listpack-value, list-max-listpack-size, set-max-listpack-entries und set-max-intset-entries. Ueberschreitet ein Objekt einen dieser Schwellenwerte, konvertiert Redis es automatisch und transparent in die vollwertige Struktur, bei Hashes und Sets etwa in eine Hashtabelle, bei Listen in eine Quicklist aus mehreren listpack-Knoten. Diese Konvertierung ist einseitig: Ein einmal konvertiertes Objekt schrumpft nicht automatisch zurueck in die kompakte Form, selbst wenn Elemente wieder entfernt werden.
Mit OBJECT ENCODING laesst sich fuer jeden Schluessel jederzeit pruefen, welche interne Repraesentation aktuell verwendet wird. Das ist besonders bei der Kapazitaetsplanung relevant: Ein Hash mit tausenden kleinen Objekten und wenigen Feldern pro Objekt bleibt im listpack-Encoding und braucht deutlich weniger Speicher als ein aequivalentes Set aus einzelnen String-Keys. Wer die Standard-Schwellenwerte kennt und bewusst unterhalb davon modelliert, etwa durch Sharding grosser Hashes in mehrere kleinere, kann den Speicherverbrauch signifikant senken.
# redis.conf: memory-encoding thresholds for compact structures
# English comments describe the effect of each threshold
# Hash switches from listpack to hashtable above these limits
hash-max-listpack-entries 128
hash-max-listpack-value 64
# List nodes switch from listpack to quicklist above this size
list-max-listpack-size 128
# Set switches from listpack/intset to hashtable above these limits
set-max-listpack-entries 128
set-max-intset-entries 512
# Inspect the current encoding of a live key
redis-cli OBJECT ENCODING user:1000
redis-cli OBJECT ENCODING tags:article:501
redis-cli OBJECT ENCODING queue:emails
7. Die richtige Datenstruktur fuer den Use Case waehlen
Die Wahl zwischen den Redis-Datentypen sollte immer von der Zugriffsmuster ausgehen, nicht vom vermeintlich einfachsten Weg. Die Leitfrage lautet: Wie wird auf die Daten zugegriffen? Braucht die Anwendung ein einzelnes Feld eines Objekts, ohne das gesamte Objekt zu laden, ist ein Hash fast immer die bessere Wahl als ein serialisierter String. Muss eine Reihenfolge erhalten bleiben und an beiden Enden schnell eingefuegt werden, ist eine Liste die richtige Struktur. Geht es nur um die Frage der Mitgliedschaft oder um Mengen-Operationen zwischen zwei Kollektionen, ist ein Set die natuerliche Wahl.
Ein weiteres Kriterium ist die erwartete Groesse. Kleine Objekte mit wenigen Feldern profitieren stark vom listpack-Encoding und sollten so modelliert werden, dass sie unter den Standard-Schwellenwerten bleiben. Sehr grosse Kollektionen mit zehntausenden Elementen sollten hingegen ueberlegt geshardet werden, etwa durch Aufteilung eines riesigen Hash in mehrere kleinere Hashes nach einem Hash-Modulo-Schema, um die Latenz einzelner Operationen niedrig zu halten und die Replikation nicht mit einem einzelnen, riesigen Schluessel zu belasten.
Ein dritter, oft unterschaetzter Faktor ist die Notwendigkeit atomarer Teiloperationen. Wenn mehrere Clients gleichzeitig auf dasselbe Objekt zugreifen und nur einzelne Felder aendern, verhindert ein Hash mit gezielten HSET-Aufrufen Race Conditions, die bei einem Read-Modify-Write-Zyklus auf einem serialisierten String-Objekt entstehen wuerden. Diese drei Kriterien, Zugriffsmuster, erwartete Groesse und Atomaritaetsbedarf, bilden zusammen eine verlaessliche Checkliste fuer die Wahl des passenden Redis-Datentyps.
8. Komplexitaetsklassen und Performance in der Praxis
Jeder Redis-Command ist mit einer Big-O-Komplexitaetsklasse dokumentiert, und diese Angabe ist keine akademische Fussnote, sondern entscheidend fuer die Performance in Produktion. HGET, LPUSH, SADD und SISMEMBER laufen alle in O(1), unabhaengig von der Groesse der zugrunde liegenden Struktur. HGETALL, LRANGE ueber die komplette Liste und SMEMBERS laufen dagegen in O(n) und koennen bei sehr grossen Kollektionen den Single-Threaded-Event-Loop von Redis fuer mehrere Millisekunden blockieren, waehrenddessen keine andere Anfrage bearbeitet wird.
Besonders gefaehrlich sind Commands wie KEYS oder ein unbedachtes SMEMBERS auf einem Set mit Millionen Elementen in Produktionsumgebungen mit hohem Durchsatz. Die Empfehlung lautet, fuer grosse Kollektionen konsequent auf die Cursor-basierten Scan-Varianten SCAN, HSCAN, SSCAN und LRANGE mit begrenztem Offset zu setzen, die die Iteration in kleine, nicht-blockierende Schritte aufteilen. Diese Commands geben pro Aufruf nur einen Ausschnitt zurueck und einen Cursor fuer den naechsten Aufruf, sodass der Event-Loop zwischen den Schritten fuer andere Clients frei bleibt.
9. Redis-Datentypen im direkten Vergleich
Die folgende Tabelle fasst die vier besprochenen Redis-Datentypen mit ihren typischen Anwendungsfaellen, der wichtigsten Komplexitaetsklasse und dem relevanten Speicher-Encoding zusammen, um die Entscheidung fuer ein konkretes Projekt zu erleichtern.
| Datentyp | Typischer Use Case | Kern-Komplexitaet | Kompaktes Encoding |
|---|---|---|---|
| String | Zaehler, Sessions, Locks | O(1) | int / embstr / raw |
| Hash | Objekte, Profile, Teil-Updates | O(1) pro Feld | listpack |
| Liste | Queues, Feeds, Log-Puffer | O(1) an Enden | listpack / quicklist |
| Set | Mitgliedschaft, Mengenlogik | O(1) Membertest | intset / listpack |
Die Tabelle zeigt, dass alle vier Redis-Datentypen fuer ihre jeweiligen Kernoperationen konstante Zeit bieten, solange man die O(n)-Fallen wie HGETALL oder SMEMBERS auf grossen Kollektionen vermeidet und stattdessen auf Scan-basierte Iteration setzt. Das kompakte Encoding ist bei allen vier Typen ein automatischer Mechanismus, der ohne Zutun greift, solange die konfigurierten Groessen-Schwellenwerte nicht ueberschritten werden.
Mironsoft
Redis-Architektur, Caching-Strategien und Magento-Performance
Redis-Datenmodell noch nicht auf den Use Case zugeschnitten?
Wir analysieren bestehende Redis-Keyspaces, identifizieren ineffiziente Modellierung und bauen Datenstrukturen, die Speicher sparen und Latenzen senken, inklusive Magento-Cache-Backends.
Keyspace-Analyse
Memory-Encoding, Schluesselgroessen und O(n)-Fallen identifizieren
Datenmodell-Redesign
Hashes, Listen und Sets statt serialisierter JSON-Strings
Magento-Integration
Redis als Cache- und Session-Backend fuer Magento 2 einrichten und tunen
10. Zusammenfassung
Die vier Kern-Redis-Datentypen Strings, Hashes, Listen und Sets loesen jeweils ein anderes Modellierungsproblem. Strings eignen sich fuer Zaehler, Sessions und einfache Werte mit optionaler TTL. Hashes buendeln zusammengehoerige Felder unter einem Schluessel und erlauben atomare Teil-Updates ohne den Overhead separater String-Keys. Listen liefern geordnete Sequenzen mit konstanter Zeit an beiden Enden und sind die Basis fuer einfache Queues. Sets beantworten Mitgliedschaftsfragen in konstanter Zeit und bieten serverseitige Mengenoperationen wie Schnittmenge und Vereinigung.
Das interne Memory-Encoding mit listpack und intset sorgt dafuer, dass kleine Instanzen dieser Redis-Datentypen automatisch kompakt gespeichert werden, solange konfigurierte Schwellenwerte nicht ueberschritten werden. Wer diese Schwellenwerte kennt und bei der Modellierung beruecksichtigt, kann den Speicherverbrauch erheblich senken, ohne auf Funktionalitaet zu verzichten. Die Wahl der richtigen Struktur sollte immer vom Zugriffsmuster ausgehen, nicht von der vermeintlich einfachsten Implementierung mit einem serialisierten String.
Redis-Datentypen im Detail: Das Wichtigste auf einen Blick
Strings
Atomare Zaehler mit INCR, TTL-basierte Sessions mit SET ... EX, Locks mit SET NX.
Hashes
Objekte mit Teil-Updates per HSET, kein Read-Modify-Write auf serialisierten Strings noetig.
Listen & Sets
Listen fuer Queues mit O(1) an beiden Enden, Sets fuer Mitgliedschaft und Mengenoperationen in O(1).
Memory-Encoding
listpack und intset sparen automatisch Speicher unterhalb der konfigurierten Schwellenwerte.