Beherrschung des Redis-Speichermanagements für Spitzenleistung
Redis ist bekannt für seine blitzschnelle Leistung, die größtenteils auf seinem In-Memory-Betrieb beruht. Um Spitzenleistungen wirklich freizusetzen und aufrechtzuerhalten, ist die Beherrschung des Redis-Speichermanagements jedoch nicht nur vorteilhaft – es ist unerlässlich. Eine unsachgemäße Speicherhandhabung kann zu allem führen, von erhöhter Latenz und reduziertem Durchsatz bis hin zu Serverabstürzen und Datenverlust. Dieser Artikel befasst sich mit den kritischen Aspekten der Verwaltung des Redis-Speichers, einschließlich Allokationsstrategien, dem Verständnis von Fragmentierung, der Optimierung von Datenstrukturen und der Konfiguration von Eviction-Richtlinien, alles mit dem Ziel, Ihnen zu helfen, die höchstmögliche Stabilität und Effizienz zu erreichen.
Effektives Speichermanagement in Redis geht über das bloße Vorhandensein von ausreichend RAM hinaus. Es beinhaltet ein tiefes Verständnis dafür, wie Redis Daten speichert, wie es Systemressourcen verbraucht und wie verschiedene Konfigurationseinstellungen seinen Speicher-Fußabdruck beeinflussen. Durch die Optimierung der Speichernutzung Ihrer Redis-Instanz können Sie deren Reaktionsfähigkeit erheblich verbessern, ihre Betriebszeit verlängern und sicherstellen, dass sie Ihre Anwendungen auch bei unterschiedlichen Lasten zuverlässig bedient. Wir werden praktische Techniken und Best Practices untersuchen, um Sie bei der Feinabstimmung Ihrer Redis-Bereitstellungen zu unterstützen.
Verständnis der Redis-Speichernutzung
Redis nutzt den Systemspeicher, um alle seine Daten zu speichern. Wenn Sie ein Schlüssel-Wert-Paar mit SET setzen, weist Redis Speicher für den Schlüssel-String und den Wert zu, zusammen mit einem gewissen Overhead für interne Datenstrukturen. Das Verständnis der verschiedenen Komponenten der Speichernutzung ist der erste Schritt zu einer effektiven Verwaltung:
- Datenspeicher (Data Memory): Dies ist der Speicher, der von Ihren tatsächlichen Daten verbraucht wird (Schlüssel, Werte und interne Datenstrukturen wie Dictionaries zur Abbildung von Schlüsseln auf Werte). Die Größe hängt von der Anzahl und Größe Ihrer Schlüssel und Werte sowie den gewählten Datenstrukturen ab (Strings, Hashes, Listen, Sets, Sorted Sets).
- Overhead-Speicher (Overhead Memory): Redis fügt für jeden Schlüssel einen gewissen Overhead hinzu (z. B. Zeiger, Metadaten für LRU/LFU-Verfolgung, Ablaufinformationen). Kleine Datenstrukturen können speziell kodiert werden (z. B.
ziplist,intset), um diesen Overhead zu reduzieren, aber größere verwenden allgemeinere (und speicherintensivere) Darstellungen. - Pufferspeicher (Buffer Memory): Redis verwendet Client-Ausgabepuffer, Replikations-Backlog-Puffer und AOF-Puffer. Große oder langsame Clients oder eine geschäftige Replikationseinrichtung können erheblichen Pufferspeicher beanspruchen.
- Fork-Speicher (Fork Memory): Wenn Redis Hintergrundoperationen wie das Speichern von RDB-Snapshots oder das Umschreiben von AOF-Dateien durchführt, erzeugt es einen Kindprozess (
fork). Dieser Kindprozess teilt sich den Speicher zunächst mit dem Elternprozess über Copy-on-Write (CoW). Alle Schreibvorgänge in den Datensatz durch den Elternprozess nach demforkführen jedoch dazu, dass Seiten dupliziert werden, was den gesamten Speicher-Fußabdruck erhöht.
Überwachung des Redis-Speichers
Die regelmäßige Überwachung des Redis-Speichers ist entscheidend, um potenzielle Probleme zu erkennen, bevor sie eskalieren. Das Hauptwerkzeug hierfür ist der Befehl INFO memory zusammen mit MEMORY USAGE.
Der Befehl INFO memory
redis-cli INFO memory
Schlüsselkennzahlen aus INFO memory:
used_memory: Die Gesamtzahl der von Redis über seinen Allokator (jemalloc, glibc usw.) zugewiesenen Bytes. Dies ist die Summe des von Ihren Daten, internen Datenstrukturen und temporären Puffern genutzten Speichers.used_memory_human:used_memoryim menschenlesbaren Format.used_memory_rss: Resident Set Size (RSS), die vom Betriebssystem gemeldete Speichermenge, die vom Redis-Prozess verbraucht wird. Dies umfasst die eigenen Zuweisungen von Redis zuzüglich des vom Speichermanagement des Betriebssystems, gemeinsam genutzter Bibliotheken und potenziell fragmentiertem Speicher, der noch nicht an das OS zurückgegeben wurde.mem_fragmentation_ratio: Dies istused_memory_rss / used_memory. Ein ideales Verhältnis liegt leicht über 1,0 (z. B. 1,03-1,05). Ein Verhältnis, das deutlich über 1,0 liegt (z. B. 1,5+), deutet auf eine hohe Speicherfragmentierung hin. Ein Verhältnis kleiner als 1,0 deutet auf Speicher-Swapping hin, was ein kritisches Leistungsproblem darstellt.allocator_frag_bytes: Fragmentierungs-Bytes, die vom Speicherallokator gemeldet werden.lazyfree_pending_objects: Anzahl der Objekte, die auf eine asynchrone Freigabe warten.
Der Befehl MEMORY USAGE
Um die Speichernutzung einzelner Schlüssel zu überprüfen:
redis-cli MEMORY USAGE mykey
redis-cli MEMORY USAGE myhashkey SAMPLES 0 # Schätzung für Aggregate
Dieser Befehl liefert eine geschätzte Speichernutzung für einen bestimmten Schlüssel und hilft Ihnen, große oder ineffizient gespeicherte Datenpunkte zu identifizieren.
Wichtige Strategien zur Speicheroptimierung
Die Optimierung des Speichers in Redis umfasst mehrere proaktive Schritte, von der Auswahl der richtigen Datentypen bis zur Verwaltung der Fragmentierung.
1. Optimierung der Datenstrukturen
Redis bietet verschiedene Datenstrukturen, die jeweils eigene Speichereigenschaften aufweisen. Die Auswahl der richtigen Struktur und deren entsprechende Konfiguration kann den Speicherverbrauch erheblich senken.
- Strings: Am einfachsten, aber achten Sie auf große Strings. Die Verwendung von
SEToderGETbei sehr großen Strings (MBs) kann aufgrund des Netzwerk- und Speicherübertragungs-Overheads die Leistung beeinträchtigen. - Hashes, Listen, Sets, Sorted Sets (Aggregate): Redis versucht, Speicher zu sparen, indem es kleine aggregierte Datentypen kompakt kodiert (z. B.
ziplistfür Hashes/Listen,intsetfür Sets von Integern). Diese kompakten Kodierungen sind sehr speichereffizient, werden aber für größere Strukturen weniger effizient und wechseln zu regulären Hash-Tabellen oder Skip-Listen.- Tipp: Halten Sie einzelne Aggregatmitglieder klein. Bei Hashes sollten Sie viele kleine Felder gegenüber wenigen großen Feldern bevorzugen.
- Konfiguration: Die Direktiven
hash-max-ziplist-entries,hash-max-ziplist-value,list-max-ziplist-entries,list-max-ziplist-value,set-max-intset-entriesundzset-max-ziplist-entries/zset-max-ziplist-valueinredis.confsteuern, wann Redis von der kompakten Kodierung zu regulären Datenstrukturen wechselt. Stimmen Sie diese sorgfältig ab; zu große Werte können die Leistung bei Zugriffsmustern beeinträchtigen, während zu kleine Werte den Speicher erhöhen können.
2. Best Practices für das Schlüsseldesign
Obwohl Werte typischerweise mehr Speicher verbrauchen, ist auch die Optimierung von Schlüsselnamen wichtig:
- Kurze, beschreibende Schlüssel: Kürzere Schlüssel sparen Speicher, besonders wenn Sie Millionen davon haben. Opfern Sie jedoch nicht die Klarheit für extreme Kürze. Streben Sie nach beschreibenden, aber prägnanten Schlüsselnamen.
- Schlecht:
user:1000:profile:details:email - Gut:
user:1000:email(wenn Sie nur die E-Mail speichern)
- Schlecht:
- Präfixe: Verwenden Sie konsistente Präfixe (z. B.
user:,product:), um die Organisation zu gewährleisten. Dies hat minimale Auswirkungen auf den Speicher, erleichtert aber die Verwaltung.
3. Minimierung des Overheads
Jeder Schlüssel und Wert hat einen gewissen internen Overhead. Die Reduzierung der Anzahl der Schlüssel, insbesondere kleiner Schlüssel, kann effektiv sein.
- Hash anstelle mehrerer Strings: Wenn Sie viele zusammengehörige Felder für eine Entität haben, speichern Sie diese in einem einzigen
HASHanstelle mehrererSTRING-Schlüssel. Dies reduziert die Anzahl der Top-Level-Schlüssel und deren zugehörigen Overhead.- Beispiel: Anstelle von
user:1:name,user:1:email,user:1:ageverwenden Sie einenHASH-Schlüsseluser:1mit den Feldernname,email,age.
- Beispiel: Anstelle von
4. Verwaltung der Speicherfragmentierung
Speicherfragmentierung tritt auf, wenn der Speicherallokator keine zusammenhängenden Speicherblöcke der exakt benötigten Größe finden kann, was zu ungenutzten Lücken führt. Dies kann dazu führen, dass used_memory_rss deutlich höher ist als used_memory.
- Ursachen: Häufiges Einfügen und Löschen von Schlüsseln unterschiedlicher Größe, insbesondere nachdem der Speicherallokator lange gelaufen ist.
- Erkennung: Ein
mem_fragmentation_ratio, der deutlich über 1,0 liegt (z. B. 1,5-2,0), deutet auf eine hohe Fragmentierung hin. - Lösungen:
- Redis 4.0+ Aktive Defragmentierung: Redis kann den Speicher aktiv ohne Neustart defragmentieren. Aktivieren Sie dies mit
activedefrag yesinredis.confund konfigurieren Sieactive-defrag-max-scan-timeundactive-defrag-cycle-min/max. Dies ermöglicht es Redis, Daten zu verschieben und den Speicher zu komprimieren. - Neustart von Redis: Die einfachste, wenn auch störende, Methode zur Speicherdefragmentierung ist der Neustart des Redis-Servers. Dadurch wird der gesamte Speicher an das Betriebssystem zurückgegeben, und der Allokator beginnt von Neuem. Stellen Sie bei persistenten Instanzen sicher, dass vor dem Neustart ein RDB-Snapshot oder eine AOF-Datei gespeichert wird.
- Redis 4.0+ Aktive Defragmentierung: Redis kann den Speicher aktiv ohne Neustart defragmentieren. Aktivieren Sie dies mit
# redis.conf-Einstellungen für die aktive Defragmentierung
activedefrag yes
active-defrag-ignore-bytes 100mb # Nicht defragmentieren, wenn die Fragmentierung weniger als 100MB beträgt
active-defrag-threshold-lower 10 # Defrag starten, wenn das Fragmentierungsverhältnis > 10% beträgt
active-defrag-threshold-upper 100 # Defrag stoppen, wenn das Fragmentierungsverhältnis > 100% beträgt
active-defrag-cycle-min 1 # Minimaler CPU-Aufwand für Defrag (1-100%)
active-defrag-cycle-max 20 # Maximaler CPU-Aufwand für Defrag (1-100%)
Eviction-Richtlinien: Verwaltung von maxmemory
Wenn Redis als Cache verwendet wird, ist es entscheidend zu definieren, was passiert, wenn der Speicher ein vordefiniertes Limit erreicht. Die Direktive maxmemory in redis.conf legt dieses Limit fest, und maxmemory-policy bestimmt die Eviction-Strategie.
maxmemory 2gb # Maximalen Speicher auf 2 Gigabyte einstellen
maxmemory-policy allkeys-lru # Die am wenigsten kürzlich verwendeten Schlüssel aus allen Schlüsseln entfernen
Übliche Optionen für maxmemory-policy:
noeviction: (Standard) Neue Schreibvorgänge werden blockiert, wennmaxmemoryerreicht ist. Lesevorgänge funktionieren weiterhin. Dies ist gut zum Debuggen, aber normalerweise nicht für Produktions-Caches.allkeys-lru: Entfernt die am wenigsten kürzlich verwendeten (LRU) Schlüssel aus allen Keyspaces (Schlüssel mit oder ohne Ablaufzeit).volatile-lru: Entfernt LRU-Schlüssel nur aus denjenigen Schlüsseln, denen eine Ablaufzeit zugewiesen wurde.allkeys-lfu: Entfernt die am wenigsten häufig verwendeten (LFU) Schlüssel aus allen Keyspaces.volatile-lfu: Entfernt LFU-Schlüssel nur aus denjenigen Schlüsseln, denen eine Ablaufzeit zugewiesen wurde.allkeys-random: Entfernt zufällig Schlüssel aus allen Keyspaces.volatile-random: Entfernt zufällig Schlüssel nur aus denjenigen Schlüsseln, denen eine Ablaufzeit zugewiesen wurde.volatile-ttl: Entfernt Schlüssel mit der kürzesten Verweildauer (TTL) nur aus denjenigen Schlüsseln, denen eine Ablaufzeit zugewiesen wurde.
Auswahl der richtigen Richtlinie:
- Für allgemeines Caching sind
allkeys-lruoderallkeys-lfuoft gute Wahlen, abhängig davon, ob Aktualität oder Häufigkeit ein besserer Indikator für die Nützlichkeit Ihrer Daten ist. - Wenn Sie Redis hauptsächlich für die Sitzungsverwaltung oder Objekte mit expliziten Ablaufzeiten verwenden, sind
volatile-lruodervolatile-ttlmöglicherweise besser geeignet.
Warnung: Wenn maxmemory-policy auf noeviction gesetzt ist und maxmemory erreicht wird, schlagen Schreibvorgänge fehl, was zu Anwendungsfehlern führt.
Persistenz und Speicher-Overhead
Redis-Persistenzmechanismen (RDB und AOF) interagieren ebenfalls mit dem Speicher:
- RDB-Snapshots: Wenn Redis eine RDB-Datei speichert, erzeugt es einen Kindprozess (
fork). Während des Snapshot-Prozesses führen alle Schreibvorgänge in den Redis-Datensatz durch den Elternprozess dazu, dass Speicherseiten aufgrund von Copy-on-Write (CoW) dupliziert werden. Dies kann den Speicher-Fußabdruck vorübergehend verdoppeln, insbesondere bei stark ausgelasteten Instanzen mit häufigen RDB-Speicherungen. - AOF-Rewrite: In ähnlicher Weise tritt beim Umschreiben der AOF-Datei (z. B.
BGREWRITEAOF) einforkauf, was zu einer vorübergehenden Speicherduplizierung führt. Der AOF-Puffer selbst verbraucht ebenfalls Speicher.
Tipp: Planen Sie RDB-Speicherungen und AOF-Rewrites nach Möglichkeit außerhalb der Spitzenzeiten oder stellen Sie sicher, dass Ihr Server über ausreichend freien RAM verfügt, um den CoW-Overhead zu bewältigen.
Lazy Freeing (Verzögerte Freigabe)
Redis 4.0 führte die verzögerte Freigabe (nicht blockierendes Löschen) ein, um zu verhindern, dass der Server beim Löschen großer Schlüssel oder beim Leeren von Datenbanken blockiert wird. Anstatt den Speicher synchron zurückzufordern, kann Redis die Aufgabe der Speicherfreigabe in einen Hintergrundthread auslagern.
lazyfree-lazy-eviction yes: Gibt Speicher bei der Eviction asynchron frei.lazyfree-lazy-expire yes: Gibt Speicher asynchron frei, wenn Schlüssel ablaufen.lazyfree-lazy-server-del yes: Gibt Speicher asynchron frei, wennDEL,RENAME,FLUSHALL,FLUSHDBbei großen Schlüsseln/Datenbanken aufgerufen werden.
Empfehlung: Aktivieren Sie die verzögerte Freigabe für ausgelastete Instanzen, um potenzielle Latenzspitzen zu reduzieren, die durch die synchrone Speicherwiederherstellung verursacht werden.
Pipelining und Speicher
Pipelining ist zwar in erster Linie eine Netzwerkoptimierungstechnik, kann sich aber indirekt auf die Speicherleistung auswirken, indem es die Befehlsverarbeitung effizienter macht. Durch das Senden mehrerer Befehle an Redis in einem einzigen Roundtrip wird die Netzwerklatenz und der CPU-Overhead pro Befehl sowohl auf Client- als auch auf Serverseite reduziert. Dies ermöglicht es Redis, mehr Operationen pro Sekunde zu verarbeiten, ohne große Befehlswarteschlangen anzuhäufen, was andernfalls zu einem höheren Speicherverbrauch in Client-Puffern oder einer langsameren Verarbeitung führen könnte, die den Speicherallokator auf Dauer belastet.
Obwohl Pipelining die Speicherzuweisung nicht direkt verwaltet, stellen seine Effizienzverbesserungen sicher, dass Redis einen höheren Durchsatz mit weniger für den Befehls-Overhead verschwendeten Ressourcen bewältigen kann, wodurch der Speicherallokator unter Last reibungsloser arbeiten kann.
Fazit
Die Beherrschung des Redis-Speichermanagements ist ein fortlaufender Prozess, der die Leistung und Stabilität Ihrer Anwendungen erheblich beeinflusst. Indem Sie verstehen, wie Redis den Speicher nutzt, seinen Fußabdruck sorgfältig überwachen, Ihre Datenstrukturen optimieren, die Fragmentierung effektiv verwalten und die Eviction-Richtlinien klug konfigurieren, können Sie sicherstellen, dass Ihre Redis-Instanzen mit maximaler Effizienz laufen.
Beginnen Sie immer mit einer klaren Überwachung und wenden Sie dann eine Kombination aus Best Practices für das Datenmodell, geeigneten Konfigurationseinstellungen und einer durchdachten Berücksichtigung von Persistenz- und Eviction-Strategien an. Überprüfen Sie regelmäßig Ihre Speichernutzungsmuster, während sich Ihre Anwendung und Ihre Daten weiterentwickeln, um eine robuste und hochleistungsfähige Redis-Umgebung aufrechtzuerhalten.