Eines der Dinge, die Benutzer, die mit anderen Datenbanken vertraut sind, oft verwirrt, wenn sie Redis ausprobieren, ist die mangelnde Sichtbarkeit der Datenbank:Es gibt keine Reihe von Tabellen oder Sammlungen Sehen Sie, nur ein einfacher, flacher Schlüsselraum, der (potenziell) Millionen von Schlüsseln haben könnte. Die Fähigkeit, diesen Schlüsselraum kostengünstig zu durchlaufen, wird daher sehr wichtig, um sich mit den Datenbankinhalten vertraut zu machen.
Das Iterieren über den Redis-Schlüsselraum hat auch andere wichtige Anwendungsfälle, von denen mir einige einfallen:
- Garbage Collection oder Aufräumen von Schlüsseln, die einem bestimmten Muster entsprechen
- Datenverschiebung und Schemaänderungen oder Verschiebung eines bestimmten Schlüsselsatzes in eine andere Datenstruktur
- Debuggen, Datensampling, Datenkorrekturen oder Finden und Reparieren aller Schlüssel, die durch eine kürzliche Änderung durcheinander gebracht wurden
In diesem Beitrag werden wir uns eingehend mit verschiedenen Schlüsselraum-Iterationsoptionen befassen, die in Redis verfügbar sind.
O(N) Iteratoren:SCHLÜSSEL
Der Redis KEYS-Befehl gibt alle Schlüssel in der Datenbank zurück, die einem Muster entsprechen (oder alle Schlüssel im Schlüsselbereich). Ähnliche Befehle zum Abrufen aller in einem Hash gespeicherten Felder sind HGETALL und für alle zum Abrufen der Mitglieder eines SMEMBERS. Die Schlüssel in Redis selbst werden in einem Wörterbuch (auch bekannt als Hash-Tabelle) gespeichert. Der KEYS-Befehl funktioniert, indem er über dieses Wörterbuch iteriert und alles, was mit dem Muster übereinstimmt, als einzelne Array-Antwort sendet. Die anderen Befehle funktionieren ähnlich.
Die Leistung einer solchen Operation hängt von der Größe der Sammlung ab, d. h. O(N). Daher wird von der Verwendung von KEYS in Produktionsumgebungen mit einer großen Anzahl von Schlüsseln dringend abgeraten. Da Redis Single-Threading ist, wird es während dieser Iteration blockiert, wodurch andere Operationen blockiert werden. Daher sollte KEYS nur zum Debuggen und zu anderen besonderen Anlässen verwendet werden, bei denen die Leistung keine Rolle spielt (z. B. wenn die Datenbank offline geschaltet wurde, um eine Datenkorrektur anzuwenden). Die andere wichtige Sache, die Sie bei diesem Algorithmus beachten sollten, ist, dass er alle übereinstimmenden Schlüssel zusammen als eine einzige Antwort sendet. Dies kann äußerst praktisch sein, wenn der Schlüsselraum klein ist, führt jedoch zu mehreren Problemen in einem großen Schlüsselraum. KEYS ist jedoch ein beliebter Befehl unter Entwicklern in ihren eigenen Entwicklungsumgebungen.
SCHLÜSSEL in Aktion:
127.0.0.1:6379[1]> MSET eins 1 zwei 2 drei 3 vier 4OK# Alle Tasten127.0.0.1:6379[1]> Tasten *1) "vier"2) "drei"3) " zwei"4) "eins"# Tasten, die mit dem Buchstaben 't' beginnen127.0.0.1:6379[1]> Tasten t*1) "drei"2) "zwei"# Tasten, die ein 'ee' enthalten127. 0.0.1:6379[1]> Tasten *ee*1) "drei"
Cursor-basierte Iteratoren:SCAN
Der SCAN und seine Schwesterbefehle SSCAN (für Sätze), HSCAN (für Hashes) und ZSCAN (für sortierte Sätze) bieten den Cursor-basierten Ansatz zum Iterieren über Redis-Datenstrukturen. Sie sind seit 2.8.0 in Redis verfügbar.
Schlüssel werden in inkrementellen Iterationen mit konstanter Zeitgarantie für jede Iteration zurückgegeben. Ein Cursor (in diesem Fall eine Ganzzahl) wird zurückgegeben, wenn die Iterationen initialisiert werden, und bei jeder Iteration wird ein aktualisierter Cursor zurückgegeben. Ein Iterationszyklus beginnt, wenn der Cursor in der SCAN-Anfrage auf 0 gesetzt wird, und endet, wenn der vom Server zurückgegebene Cursor 0 ist. Aufgrund von Nuancen der Redis-Architektur und der Implementierung des Cursor-Algorithmus sind hier einige Besonderheiten dieses Ansatzes aufgeführt:
- Eine vollständige Iteration ruft immer alle Elemente ab, die in der Sammlung vom Anfang bis zum Ende einer vollständigen Iteration vorhanden waren.
- Eine vollständige Iteration gibt niemals ein Element zurück, das vom Anfang bis zum Ende einer vollständigen Iteration NICHT in der Sammlung vorhanden war.
- Ein bestimmtes Element kann mehrfach zurückgegeben werden. Es ist Sache der Anwendung, den Fall von doppelten Elementen zu behandeln
- Elemente, die während einer vollständigen Iteration nicht ständig in der Sammlung vorhanden waren, können zurückgegeben werden oder nicht:Sie sind undefiniert.
- Eine Anzahl von Elementen, die während jeder Zählung zurückgegeben werden, variiert und kann auch 0 sein. Die Iteration ist jedoch erst abgeschlossen, wenn der Server den Cursorwert 0 zurückgibt.
- Der COUNT Die Option kann verwendet werden, um die Anzahl der Elemente zu begrenzen, die in jeder Iteration zurückgegeben werden. Der Standardwert ist 10. Er wird jedoch nur als Vorschlag betrachtet und nicht in allen Fällen erzwungen. Der COUNT-Wert kann bei jedem Iterationsaufruf geändert werden.
- Das MATCH Die Option erlaubt die Angabe von Mustern, wie es der KEYS-Befehl erlaubt.
- Die Cursor-Implementierung ist serverseitig vollständig zustandslos. Dadurch können (potentiell) unendliche Iterationen parallel gestartet werden. Außerdem gibt es keine Anforderungen, um sicherzustellen, dass eine Iteration bis zum Ende fortgesetzt wird und jederzeit gestoppt werden kann.
Trotz seiner Besonderheiten ist SCAN ein sehr nützlicher Befehl und für die meisten Anwendungsfälle der richtige Befehl für Schlüsselraum-Iterationen.
SCAN ist ein sehr nützlicher Befehl und der richtige Befehl für Schlüsselraum-Iterationen in #RedisClick To TweetSCAN in Aktion
127.0.0.1:6379[1]> flushdbOK127.0.0.1:6379[1]> Schlüssel *(leere Liste oder Set)127.0.0.1:6379[1]> debuggen 33OK127.0.0.1:6379[ 1]> scan 0 COUNT 51) "4"2) 1) "key:1" 2) "key:9" 3) "key:13" 4) "key:29" 5) "key:23"127.0. 0.1:6379[1]> scan 4 1) "42"2) 1) "key:24" 2) "key:28" 3) "key:18" 4) "key:16" 5) "key:12 " 6) "Schlüssel:2" 7) "Schlüssel:6" 8) "Schlüssel:31" 9) "Schlüssel:27" 10) "Schlüssel:19"127.0.0.1:6379[1]> scan 421) "9 "2) 1) "Schlüssel:3" 2) "Schlüssel:4" 3) "Schlüssel:20" 4) "Schlüssel:8" 5) "Schlüssel:32" 6) "Schlüssel:5" 7) "Schlüssel:26" 8) "Schlüssel:10" 9) "Schlüssel:21" 10) "Schlüssel:14"127.0.0.1:6379[1]> scan 9 COUNT 1001) "0"2) 1) "Schlüssel:25" 2 ) "Taste:30" 3) "Taste:22" 4) "Taste:17" 5) "Taste:15" 6) "Taste:0" 7) "Taste:11" 8) "Taste:7"Unter der Haube
Der Algorithmus, den SCAN (und seine Schwesterbefehle) zum Scannen verwenden, ist faszinierend und führt zu einigen der Eigenschaften des oben beschriebenen Befehls. Antirez hat es in seinem Blogbeitrag auf hohem Niveau beschrieben und es wird (etwas besser) in den Kommentaren über der Implementierung (Funktion dictScan) erklärt. Eine detaillierte Beschreibung würde diesen Beitrag zu lang machen, daher werde ich eine ausreichende Beschreibung geben, um die Auswirkungen deutlich zu machen.
- Die meisten Redis-Datenstrukturen werden intern als Wörterbücher dargestellt (zumindest teilweise im Fall von sortierten Sätzen). Sie sind als Zweierpotenz-Hash-Tabellen mit Verkettung für Kollisionen implementiert. Die Herausforderung beim Schreiben eines Cursor-basierten iterativen Algorithmus besteht darin, mit dem Wachsen und Schrumpfen des Hashs umgehen zu können, ohne die Redis-Prinzipien der Einfachheit (der API) und Geschwindigkeit zu opfern.
- SCAN scannt grundsätzlich bei jeder Iteration eine Reihe von Hash-Buckets und gibt die Elemente zurück, die dem Muster in diesen entsprechen. Da nur eine feste Liste von Buckets betrachtet wird, können Iterationen manchmal überhaupt keine Werte zurückgeben.
- Der zurückgegebene Cursor ist im Grunde ein Offset in die durchlaufene Hash-Tabelle. Es befasst sich mit dem Wachsen und Schrumpfen von Hash-Tabellen (d. h. Rehashing) durch geschickte Manipulation von Bits höherer Ebene des Offsets, während das Offset zusammen mit den Eigenschaften der Hash-Tabelle erhöht wird. Implikationen aus diesem Ansatz sind, dass neue Elemente, die während der Iteration hinzugefügt wurden, zurückgegeben werden können oder nicht. Der Cursor selbst müsste jedoch bei einer Größenänderung der Hash-Tabelle nicht neu gestartet werden.
- Ein bestimmter Bucket muss nur einmal besucht werden und alle seine Schlüssel müssen auf einmal zurückgegeben werden. Auch hier soll sichergestellt werden, dass die Hash-Größenänderung (d. h. Rehashing) den Iterationsfortschritt nicht erschwert. Dies führt jedoch dazu, dass das COUNT-Argument nicht streng durchsetzbar ist.
- Da der obige Ansatz auf der Serverseite völlig zustandslos ist, bedeutet dies im Grunde, dass Iterationen gestoppt oder eine große Anzahl von Iterationen parallel gestartet werden können, ohne dass die Speichernutzung erhöht wird.
Zusammenfassung
Auf hoher Ebene stehen zwei Möglichkeiten zur Verfügung, um über den Redis-Schlüsselraum zu iterieren:
- Verwenden Sie KEYS, wenn die Leistung keine Rolle spielt oder wenn der Schlüsselraum angemessen groß ist.
- Verwenden Sie zu allen anderen Zeiten SCAN.
Wussten Sie, dass wir jetzt Hosting für Redis™* unterstützen? Holen Sie sich vollständig verwaltetes Hosting für Redis™ in der Sicherheit Ihres eigenen Cloud-Kontos und nutzen Sie AWS/Azure-Guthaben für Ihre Redis™-Bereitstellungen.