Verfügbar seit 2.8.0.
Zeitaufwand: O(1) für jeden Aufruf. O(N) für eine komplette Iteration, einschließlich genügend Befehlsaufrufe, damit der Cursor wieder auf 0 zurückkehrt. N ist die Anzahl der Elemente in der Sammlung.
Der SCAN-Befehl und die eng verwandten Befehle SSCAN, HSCAN und ZSCAN werden verwendet, um inkrementell über eine Sammlung von Elementen zu iterieren.
- SCAN iteriert die Menge der Schlüssel in der aktuell ausgewählten Redis-Datenbank.
- SSCAN iteriert Elemente des Typs Sets.
- HSCAN iteriert Felder des Typs Hash und deren zugehörige Werte.
- ZSCAN iteriert Elemente des Typs Sorted Set und deren zugehörige Werte.
Da diese Befehle eine inkrementelle Iteration ermöglichen und nur eine kleine Anzahl von Elementen pro Aufruf zurückgeben, können sie in der Produktion ohne den Nachteil von Befehlen wie KEYS oder SMEMBERS verwendet werden, die den Server für eine lange Zeit (sogar mehrere Sekunden) blockieren können, wenn sie gegen große Sammlungen von Schlüsseln oder Elementen aufgerufen werden.
Während jedoch blockierende Befehle wie SMEMBERS in der Lage sind, alle Elemente bereitzustellen, die zu einem bestimmten Zeitpunkt Teil einer Menge sind, bietet die SCAN-Befehlsfamilie nur begrenzte Garantien für die zurückgegebenen Elemente, da sich die Sammlung, die wir inkrementell iterieren, während des Iterationsprozesses ändern kann.
Beachten Sie, dass SCAN, SSCAN, HSCAN und ZSCAN alle sehr ähnlich funktionieren, so dass diese Dokumentation alle vier Befehle abdeckt. Ein offensichtlicher Unterschied besteht jedoch darin, dass bei SSCAN, HSCAN und ZSCAN das erste Argument der Name des Schlüssels ist, der den Set-, Hash- oder Sorted-Set-Wert enthält. Der SCAN-Befehl benötigt kein Argument für den Schlüsselnamen, da er die Schlüssel in der aktuellen Datenbank durchläuft, so dass das durchlaufene Objekt die Datenbank selbst ist.
- *SCAN basic usage
- *Scan-Garantien
- *Anzahl der bei jedem SCAN-Aufruf zurückgegebenen Elemente
- *Die Option COUNT
- *Die Option MATCH
- *Die Option TYP
- *Mehrere parallele Iterationen
- *Abbruch von Iterationen in der Mitte
- *Aufruf von SCAN mit einem beschädigten Cursor
- *Beendigungsgarantie
- *Warum kann SCAN alle Elemente eines aggregierten Datentyps in einem einzigen Aufruf zurückgeben?
- *Return value
- *History
- *Additional examples
*SCAN basic usage
SCAN ist ein cursorbasierter Iterator. Das bedeutet, dass der Server bei jedem Aufruf des Befehls einen aktualisierten Cursor zurückliefert, den der Benutzer beim nächsten Aufruf als Cursor-Argument verwenden muss.
Eine Iteration beginnt, wenn der Cursor auf 0 gesetzt wird, und endet, wenn der vom Server zurückgelieferte Cursor 0 ist. Nachfolgend ein Beispiel für eine SCAN-Iteration:
redis 127.0.0.1:6379> scan 0 1) "17" 2) 1) "key:12" 2) "key:8" 3) "key:4" 4) "key:14" 5) "key:16" 6) "key:17" 7) "key:15" 8) "key:10" 9) "key:3" 10) "key:7" 11) "key:1" redis 127.0.0.1:6379> scan 17 1) "0" 2) 1) "key:5" 2) "key:18" 3) "key:0" 4) "key:2" 5) "key:19" 6) "key:13" 7) "key:6" 8) "key:9" 9) "key:11"
Im obigen Beispiel verwendet der erste Aufruf Null als Cursor, um die Iteration zu starten. Der zweite Aufruf verwendet den vom vorherigen Aufruf zurückgegebenen Cursor als erstes Element der Antwort, also 17.
Wie Sie sehen, ist der SCAN-Rückgabewert ein Array mit zwei Werten: Der erste Wert ist der neue Cursor, der beim nächsten Aufruf verwendet werden soll, der zweite Wert ist ein Array mit Elementen.
Da beim zweiten Aufruf der zurückgegebene Cursor 0 ist, hat der Server dem Aufrufer signalisiert, dass die Iteration beendet und die Sammlung vollständig untersucht wurde. Eine Iteration mit einem Cursorwert von 0 zu beginnen und SCAN so lange aufzurufen, bis der zurückgegebene Cursor wieder 0 ist, wird als vollständige Iteration bezeichnet.
*Scan-Garantien
Der SCAN-Befehl und die anderen Befehle der SCAN-Familie sind in der Lage, dem Benutzer eine Reihe von Garantien zu bieten, die mit vollständigen Iterationen verbunden sind.
- Eine vollständige Iteration ruft immer alle Elemente ab, die in der Sammlung vom Beginn bis zum Ende einer vollständigen Iteration vorhanden waren. Das bedeutet, dass, wenn ein bestimmtes Element in der Sammlung ist, wenn eine Iteration gestartet wird, und immer noch da ist, wenn eine Iteration beendet wird, dann hat SCAN es irgendwann an den Benutzer zurückgegeben.
- Eine vollständige Iteration gibt niemals ein Element zurück, das vom Start bis zum Ende einer vollständigen Iteration NICHT in der Sammlung vorhanden war. Wenn also ein Element vor dem Beginn einer Iteration entfernt wurde und während der gesamten Dauer einer Iteration nie wieder in die Sammlung aufgenommen wird, stellt SCAN sicher, dass dieses Element nie zurückgegeben wird.
Da SCAN jedoch nur einen sehr geringen Status hat (nur den Cursor), hat es die folgenden Nachteile:
- Ein bestimmtes Element kann mehrfach zurückgegeben werden. Es liegt an der Anwendung, den Fall von doppelten Elementen zu behandeln, z.B. nur die zurückgegebenen Elemente zu verwenden, um Operationen durchzuführen, die sicher sind, wenn sie mehrfach angewendet werden.
- Elemente, die während einer vollständigen Iteration nicht ständig in der Sammlung vorhanden waren, können zurückgegeben werden oder nicht: es ist undefiniert.
*Anzahl der bei jedem SCAN-Aufruf zurückgegebenen Elemente
Die Funktionen der SCAN-Familie garantieren nicht, dass die Anzahl der pro Aufruf zurückgegebenen Elemente in einem bestimmten Bereich liegt. Die Befehle dürfen auch Nullelemente zurückgeben, und der Client sollte die Iteration nicht als abgeschlossen betrachten, solange der zurückgegebene Cursor nicht Null ist.
Die Anzahl der zurückgegebenen Elemente ist jedoch angemessen, d.h. in der Praxis kann SCAN eine maximale Anzahl von Elementen in der Größenordnung von einigen Dutzend Elementen zurückgeben, wenn eine große Sammlung iteriert wird, oder alle Elemente der Sammlung in einem einzigen Aufruf zurückgeben, wenn die iterierte Sammlung klein genug ist, um intern als kodierte Datenstruktur dargestellt zu werden (dies geschieht bei kleinen Mengen, Hashes und sortierten Mengen).
Es gibt jedoch eine Möglichkeit für den Benutzer, die Größenordnung der Anzahl der zurückgegebenen Elemente pro Aufruf mit der Option COUNT einzustellen.
*Die Option COUNT
Während SCAN keine Garantien über die Anzahl der bei jeder Iteration zurückgegebenen Elemente bietet, ist es möglich, das Verhalten von SCAN mit der Option COUNT empirisch einzustellen. Grundsätzlich gibt der Benutzer mit COUNT an, wie viel Arbeit bei jedem Aufruf geleistet werden soll, um Elemente aus der Sammlung abzurufen.
- Der Standardwert für COUNT ist 10.
- Bei der Iteration des Schlüsselraums oder einer Menge, eines Hashes oder einer sortierten Menge, die groß genug ist, um durch eine Hash-Tabelle repräsentiert zu werden, wird der Server normalerweise count oder etwas mehr als count Elemente pro Aufruf zurückgeben, vorausgesetzt, es wird keine MATCH Option verwendet. Bitte lesen Sie den Abschnitt „Warum SCAN alle Elemente auf einmal zurückgeben kann“ weiter unten in diesem Dokument.
- Bei der Iteration von Sets, die als Intsets (kleine Sets, die nur aus ganzen Zahlen bestehen) kodiert sind, oder Hashes und Sorted Sets, die als Ziplists (kleine Hashes und Sets, die aus kleinen Einzelwerten bestehen) kodiert sind, werden in der Regel alle Elemente beim ersten SCAN-Aufruf zurückgegeben, unabhängig vom COUNT-Wert.
Wichtig: Es ist nicht notwendig, bei jeder Iteration den gleichen COUNT-Wert zu verwenden. Es steht dem Aufrufer frei, die Zählung von einer Iteration zur nächsten zu ändern, solange der Cursor, der beim nächsten Aufruf übergeben wird, derjenige ist, der beim vorherigen Aufruf des Befehls erhalten wurde.
*Die Option MATCH
Es ist möglich, nur Elemente zu iterieren, die einem bestimmten glob-style Muster entsprechen, ähnlich dem Verhalten des KEYS-Befehls, der ein Muster als einziges Argument annimmt.
Zu diesem Zweck werden einfach die MATCH <pattern>
Argumente am Ende des SCAN-Befehls angehängt (dies funktioniert mit allen Befehlen der SCAN-Familie).
Dies ist ein Beispiel für die Iteration mit MATCH:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood (integer) 6 redis 127.0.0.1:6379> sscan myset 0 match f* 1) "0" 2) 1) "foo" 2) "feelsgood" 3) "foobar" redis 127.0.0.1:6379>
Es ist wichtig zu beachten, dass der MATCH-Filter angewendet wird, nachdem die Elemente aus der Sammlung abgerufen wurden, kurz bevor die Daten an den Client zurückgegeben werden. Das bedeutet, dass SCAN in den meisten Iterationen keine Elemente zurückgeben wird, wenn das Muster nur sehr wenige Elemente in der Sammlung enthält. Ein Beispiel wird unten gezeigt:
redis 127.0.0.1:6379> scan 0 MATCH *11* 1) "288" 2) 1) "key:911" redis 127.0.0.1:6379> scan 288 MATCH *11* 1) "224" 2) (empty list or set) redis 127.0.0.1:6379> scan 224 MATCH *11* 1) "80" 2) (empty list or set) redis 127.0.0.1:6379> scan 80 MATCH *11* 1) "176" 2) (empty list or set) redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000 1) "0" 2) 1) "key:611" 2) "key:711" 3) "key:118" 4) "key:117" 5) "key:311" 6) "key:112" 7) "key:111" 8) "key:110" 9) "key:113" 10) "key:211" 11) "key:411" 12) "key:115" 13) "key:116" 14) "key:114" 15) "key:119" 16) "key:811" 17) "key:511" 18) "key:11" redis 127.0.0.1:6379>
Wie Sie sehen, haben die meisten Aufrufe null Elemente zurückgegeben, aber der letzte Aufruf, bei dem ein COUNT von 1000 verwendet wurde, um den Befehl zu zwingen, mehr Scans für diese Iteration durchzuführen.
*Die Option TYP
Ab Version 6.0 können Sie diese Option verwenden, um SCAN aufzufordern, nur Objekte zurückzugeben, die mit einem bestimmten type
übereinstimmen, so dass Sie die Datenbank auf der Suche nach Schlüsseln eines bestimmten Typs durchlaufen können. Die Option TYPE ist nur für den SCAN der gesamten Datenbank verfügbar, nicht für HSCAN oder ZSCAN usw.
Das Argument type
ist derselbe Stringname, den der Befehl TYPE zurückgibt. Beachten Sie eine Eigenart, bei der einige Redis-Typen, wie GeoHashes, HyperLogLogs, Bitmaps und Bitfields, intern mit anderen Redis-Typen, wie einem String oder ZSET, implementiert werden können, so dass sie von SCAN nicht von anderen Schlüsseln des gleichen Typs unterschieden werden können. Zum Beispiel ein ZSET und GEOHASH:
redis 127.0.0.1:6379> GEOADD geokey 0 0 value (integer) 1 redis 127.0.0.1:6379> ZADD zkey 1000 value (integer) 1 redis 127.0.0.1:6379> TYPE geokey zset redis 127.0.0.1:6379> TYPE zkey zset redis 127.0.0.1:6379> SCAN 0 TYPE zset 1) "0" 2) 1) "geokey" 2) "zkey"
Es ist wichtig zu beachten, dass der TYP-Filter auch angewendet wird, nachdem Elemente aus der Datenbank abgerufen wurden, so dass die Option nicht die Menge an Arbeit reduziert, die der Server tun muss, um eine vollständige Iteration abzuschließen, und für seltene Typen erhalten Sie möglicherweise keine Elemente in vielen Iterationen.
*Mehrere parallele Iterationen
Es ist möglich, dass eine unendliche Anzahl von Clients dieselbe Sammlung gleichzeitig iterieren, da der vollständige Zustand des Iterators im Cursor liegt, der bei jedem Aufruf abgerufen und an den Client zurückgegeben wird.
*Abbruch von Iterationen in der Mitte
Da es auf der Serverseite keinen Zustand gibt, aber der Cursor den vollen Zustand erfasst, kann der Aufrufer eine Iteration auf halbem Weg abbrechen, ohne dies dem Server in irgendeiner Weise zu signalisieren. Eine unendliche Anzahl von Iterationen kann ohne Probleme gestartet und nie beendet werden.
*Aufruf von SCAN mit einem beschädigten Cursor
Der Aufruf von SCAN mit einem defekten, negativen, außerhalb des Bereichs liegenden oder anderweitig ungültigen Cursor führt zu undefiniertem Verhalten, aber niemals zu einem Absturz. Was undefiniert ist, ist, dass die Garantien für die zurückgegebenen Elemente nicht mehr von der SCAN-Implementierung gewährleistet werden können.
Die einzigen gültigen Cursor, die verwendet werden können, sind:
- Der Cursorwert von 0, wenn eine Iteration gestartet wird.
- Der Cursor, der vom vorherigen Aufruf von SCAN zurückgegeben wurde, um die Iteration fortzusetzen.
*Beendigungsgarantie
Der SCAN-Algorithmus wird nur dann garantiert beendet, wenn die Größe der iterierten Sammlung auf eine vorgegebene Maximalgröße begrenzt bleibt, andernfalls kann die Iteration einer ständig wachsenden Sammlung dazu führen, dass SCAN nie eine vollständige Iteration beendet.
Dies ist intuitiv leicht zu erkennen: wenn die Sammlung wächst, gibt es immer mehr Arbeit zu tun, um alle möglichen Elemente zu besuchen, und die Fähigkeit, die Iteration zu beenden, hängt von der Anzahl der Aufrufe von SCAN und seinem COUNT-Optionswert ab, verglichen mit der Rate, mit der die Sammlung wächst.
*Warum kann SCAN alle Elemente eines aggregierten Datentyps in einem einzigen Aufruf zurückgeben?
In der COUNT
Optionsdokumentation wird angegeben, dass diese Befehlsfamilie manchmal alle Elemente einer Menge, eines Hash oder einer sortierten Menge in einem einzigen Aufruf zurückgeben kann, unabhängig vom COUNT
Optionswert. Der Grund dafür ist, dass der Cursor-basierte Iterator nur implementiert werden kann und nützlich ist, wenn der aggregierte Datentyp, den wir scannen, als Hash-Tabelle dargestellt wird. Redis verwendet jedoch eine Speicheroptimierung, bei der kleine Aggregatdatentypen bis zum Erreichen einer bestimmten Anzahl von Elementen oder einer bestimmten maximalen Größe von Einzelelementen durch eine kompakte gepackte Kodierung mit einer einzigen Zuweisung dargestellt werden. In diesem Fall kann SCAN keinen sinnvollen Cursor zurückgeben und muss die gesamte Datenstruktur auf einmal durchlaufen, so dass das einzig sinnvolle Verhalten darin besteht, alles in einem Aufruf zurückzugeben.
Sobald die Datenstrukturen jedoch größer sind und echte Hashtabellen verwendet werden, greift die SCAN-Befehlsfamilie auf das normale Verhalten zurück. Da dieses spezielle Verhalten, alle Elemente zurückzugeben, nur für kleine Aggregate gilt, hat es keine Auswirkungen auf die Komplexität oder Latenz des Befehls. Die genauen Grenzwerte für die Umwandlung in echte Hash-Tabellen sind jedoch vom Benutzer konfigurierbar, so dass die maximale Anzahl der Elemente, die in einem einzigen Aufruf zurückgegeben werden können, davon abhängt, wie groß ein Aggregat-Datentyp sein kann und trotzdem die gepackte Darstellung verwendet werden kann.
Auch ist zu beachten, dass dieses Verhalten spezifisch für SSCAN, HSCAN und ZSCAN ist. SCAN itself never shows this behavior because the key space is always represented by hash tables.
*Return value
SCAN, SSCAN, HSCAN and ZSCAN return a two elements multi-bulk reply, where the first element is a string representing an unsigned 64 bit number (the cursor), and the second element is a multi-bulk with an array of elements.
- SCAN array of elements is a list of keys.
- SSCAN array of elements is a list of Set members.
- HSCAN array of elements contain two elements, a field and a value, for every returned element of the Hash.
- ZSCAN array of elements contain two elements, a member and its associated score, for every returned element of the sorted set.
*History
-
>= 6.0
: Supports the TYPE subcommand.
*Additional examples
Iteration of a Hash value.
redis 127.0.0.1:6379> hmset hash name Jack age 33 OK redis 127.0.0.1:6379> hscan hash 0 1) "0" 2) 1) "name" 2) "Jack" 3) "age" 4) "33"