Beschikbaar sinds 2.8.0.
Tijdcomplexiteit: O(1) voor elke aanroep. O(N) voor een volledige iteratie, inclusief genoeg commando aanroepen om de cursor terug te laten keren naar 0. N is het aantal elementen in de verzameling.
Het SCAN commando en de nauw verwante commando’s SSCAN, HSCAN en ZSCAN worden gebruikt om incrementeel over een verzameling elementen te itereren.
- SCAN itereert de verzameling sleutels in de huidig geselecteerde Redis database.
- SSCAN itereert elementen van Sets types.
- HSCAN itereert velden van Hash types en hun geassocieerde waarden.
- ZSCAN itereert elementen van Sorted Set types en hun geassocieerde scores.
Doordat deze commando’s incrementele iteratie mogelijk maken, waarbij slechts een klein aantal elementen per aanroep wordt geretourneerd, kunnen ze in produktie worden gebruikt zonder het nadeel van commando’s als KEYS of SMEMBERS die de server voor lange tijd kunnen blokkeren (zelfs enkele seconden) wanneer ze worden aangeroepen tegen grote verzamelingen sleutels of elementen.
Hoewel blokkerende commando’s als SMEMBERS in staat zijn om alle elementen die op een bepaald moment deel uitmaken van een Set te leveren, biedt de SCAN-familie van commando’s slechts beperkte garanties over de geretourneerde elementen, omdat de verzameling die we incrementeel itereren tijdens het iteratieproces kan veranderen.
Merk op dat SCAN, SSCAN, HSCAN en ZSCAN alle op vergelijkbare wijze werken, dus deze documentatie behandelt alle vier de commando’s. Een duidelijk verschil is echter dat in het geval van SSCAN, HSCAN en ZSCAN het eerste argument de naam is van de sleutel die de Set-, Hash- of Sorted Set-waarde bevat. Het SCAN commando heeft geen sleutelnaam-argument nodig omdat het sleutels in de huidige database itereert, dus het itererende object is de database zelf.
- *SCAN basisgebruik
- *Scan garanties
- *Aantal geretourneerde elementen bij elke SCAN-aanroep
- *De COUNT optie
- *De MATCH optie
- *De TYPE-optie
- *Meerdere parallelle iteraties
- *Terminating iterations in the middle
- *Aanroepen SCAN met een beschadigde cursor
- *Garantie van beëindiging
- *Waarom kan SCAN alle elementen van een geaggregeerd gegevenstype in één keer retourneren?
- *Return value
- *History
- *Additional examples
*SCAN basisgebruik
SCAN is een cursor-gebaseerde iterator. Dit betekent dat bij elke aanroep van het commando, de server een bijgewerkte cursor teruggeeft die de gebruiker moet gebruiken als cursor argument in de volgende aanroep.
Een iteratie begint wanneer de cursor op 0 wordt gezet, en eindigt wanneer de door de server teruggegeven cursor 0 is. Het volgende is een voorbeeld van SCAN iteratie:
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"
In het bovenstaande voorbeeld, gebruikt de eerste aanroep nul als cursor, om de iteratie te starten. De tweede aanroep gebruikt de cursor geretourneerd door de vorige aanroep als het eerste element van het antwoord, dat is 17.
Zoals je kunt zien is de SCAN return waarde een array van twee waarden: de eerste waarde is de nieuwe cursor om te gebruiken in de volgende aanroep, de tweede waarde is een array van elementen.
Omdat in de tweede aanroep de geretourneerde cursor 0 is, heeft de server aan de aanroeper gesignaleerd dat de iteratie is afgelopen, en dat de collectie volledig is verkend. Het starten van een iteratie met een cursorwaarde van 0, en het aanroepen van SCAN totdat de teruggestuurde cursor weer 0 is, wordt een volledige iteratie genoemd.
*Scan garanties
Het SCAN commando, en de andere commando’s in de SCAN familie, zijn in staat om de gebruiker een set garanties te bieden die geassocieerd zijn met volledige iteraties.
- Een volledige iteratie haalt altijd alle elementen op die in de collectie aanwezig waren van het begin tot het einde van een volledige iteratie. Dit betekent dat als een bepaald element in de verzameling zit als een iteratie wordt gestart, en er nog steeds is als een iteratie eindigt, SCAN het op een gegeven moment aan de gebruiker heeft teruggegeven.
- Een volledige iteratie retourneert nooit een element dat NIET in de verzameling aanwezig was van het begin tot het eind van een volledige iteratie. Dus als een element voor het begin van een iteratie is verwijderd, en nooit meer aan de collectie wordt toegevoegd gedurende de hele tijd dat een iteratie duurt, zorgt SCAN ervoor dat dit element nooit wordt geretourneerd.
Omdat SCAN zeer weinig state heeft (alleen de cursor) heeft het de volgende nadelen:
- Een gegeven element kan meerdere keren worden geretourneerd. Het is aan de applicatie om het geval van gedupliceerde elementen te behandelen, bijvoorbeeld door alleen de geretourneerde elementen te gebruiken om operaties uit te voeren die veilig zijn als ze meerdere keren worden toegepast.
- Elementen die niet constant in de verzameling aanwezig waren tijdens een volledige iteratie, kunnen worden geretourneerd of niet: het is ongedefinieerd.
*Aantal geretourneerde elementen bij elke SCAN-aanroep
De functies van de SCAN-familie garanderen niet dat het aantal geretourneerde elementen per aanroep binnen een bepaald bereik ligt. De opdrachten mogen ook nul elementen retourneren, en de client moet de iteratie niet als voltooid beschouwen zolang de geretourneerde cursor niet nul is.
Hoewel het aantal geretourneerde elementen redelijk is, dat wil zeggen, in praktische termen kan SCAN een maximum aantal elementen in de orde van enkele tientallen retourneren wanneer een grote verzameling wordt ge-iterateerd, of alle elementen van de verzameling in een enkele aanroep retourneren wanneer de ge-iterateerde verzameling klein genoeg is om intern te worden gerepresenteerd als een gecodeerde gegevensstructuur (dit gebeurt voor kleine verzamelingen, hashes en gesorteerde verzamelingen).
Er is echter een manier voor de gebruiker om de orde van grootte van het aantal geretourneerde elementen per aanroep af te stellen met behulp van de COUNT optie.
*De COUNT optie
Terwijl SCAN geen garanties geeft over het aantal geretourneerde elementen bij elke iteratie, is het mogelijk om het gedrag van SCAN empirisch aan te passen met behulp van de COUNT optie. In principe specificeert de gebruiker met COUNT de hoeveelheid werk die bij elke aanroep moet worden verricht om elementen uit de verzameling op te halen. Dit is slechts een hint voor de implementatie, maar over het algemeen is dit wat je kunt verwachten van de implementatie.
- De standaard COUNT waarde is 10.
- Bij het itereren van de sleutelruimte, of een Set, Hash of Sorted Set die groot genoeg is om te worden gerepresenteerd door een hash tabel, ervan uitgaande dat er geen MATCH optie is gebruikt, zal de server meestal count of iets meer dan count elementen per aanroep teruggeven. Kijk waarom SCAN alle elementen in één keer kan teruggeven verderop in dit document.
- Bij het itereren van Sets die zijn gecodeerd als intsets (kleine verzamelingen die alleen uit gehele getallen bestaan), of Hashes en Sorted Sets die zijn gecodeerd als ziplists (kleine hashes en sets die bestaan uit kleine individuele waarden), worden gewoonlijk alle elementen teruggegeven in de eerste SCAN-aanroep, ongeacht de COUNT-waarde.
Belangrijk: het is niet nodig om voor elke iteratie dezelfde COUNT-waarde te gebruiken. De aanroeper is vrij om de telling van de ene iteratie naar de andere te veranderen, zolang de cursor die in de volgende aanroep wordt doorgegeven maar die is van de vorige aanroep van het commando.
*De MATCH optie
Het is mogelijk om alleen elementen te itereren die overeenkomen met een gegeven glob-style patroon, vergelijkbaar met het gedrag van het KEYS commando dat een patroon als enig argument neemt.
Om dit te doen, voeg je de MATCH <pattern>
argumenten toe aan het eind van het SCAN commando (het werkt met alle SCAN familie commando’s).
Dit is een voorbeeld van iteratie met behulp van 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>
Het is belangrijk op te merken dat het MATCH filter wordt toegepast nadat elementen zijn opgehaald uit de collectie, net voordat gegevens worden teruggestuurd naar de client. Dit betekent dat als het patroon overeenkomt met zeer weinig elementen in de verzameling, SCAN waarschijnlijk geen elementen zal retourneren in de meeste iteraties. Hieronder zie je een voorbeeld:
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>
Zoals je ziet leveren de meeste oproepen nul elementen op, maar de laatste oproep waar een COUNT van 1000 werd gebruikt om het commando te dwingen meer te scannen voor die iteratie.
*De TYPE-optie
Sinds versie 6.0 kunt u deze optie gebruiken om SCAN te vragen alleen objecten terug te geven die overeenkomen met een bepaald type
, zodat u door de database kunt lopen op zoek naar sleutels van een specifiek type. De TYPE optie is alleen beschikbaar op de hele-database SCAN, niet HSCAN of ZSCAN etc.
Het type
argument is dezelfde string naam die het TYPE commando retourneert. Let op een eigenaardigheid waarbij sommige Redis types, zoals GeoHashes, HyperLogLogs, Bitmaps, en Bitfields, intern geïmplementeerd kunnen zijn met andere Redis types, zoals een string of zset, en dus niet onderscheiden kunnen worden van andere sleutels van datzelfde type door SCAN. Bijvoorbeeld een ZSET en 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"
Het is belangrijk op te merken dat het TYPE filter ook wordt toegepast nadat elementen uit de database zijn opgehaald, dus de optie vermindert niet de hoeveelheid werk die de server moet doen om een volledige iteratie te voltooien, en voor zeldzame types kan het zijn dat je in veel iteraties geen elementen ontvangt.
*Meerdere parallelle iteraties
Het is mogelijk voor een oneindig aantal clients om dezelfde collectie op hetzelfde moment te itereren, omdat de volledige status van de iterator in de cursor zit, die bij iedere aanroep wordt opgehaald en teruggegeven aan de client. Aan de server-kant wordt helemaal geen status meegenomen.
*Terminating iterations in the middle
Omdat er geen status aan de server-kant is, maar de volledige status wordt opgevangen door de cursor, is de aanroeper vrij om een iteratie halverwege te beëindigen zonder dit op enigerlei wijze aan de server te melden. Een oneindig aantal iteraties kan worden gestart en nooit worden beëindigd zonder enig probleem.
*Aanroepen SCAN met een beschadigde cursor
Aanroepen SCAN met een kapotte, negatieve, buiten bereik, of anderszins ongeldige cursor, zal resulteren in ongedefinieerd gedrag maar nooit in een crash. Wat ongedefinieerd zal zijn, is dat de garanties over de geretourneerde elementen niet langer kunnen worden gegarandeerd door de SCAN implementatie.
De enige geldige cursors om te gebruiken zijn:
- De cursor waarde van 0 bij het starten van een iteratie.
- De cursor geretourneerd door de vorige aanroep van SCAN om de iteratie voort te zetten.
*Garantie van beëindiging
Het SCAN-algoritme eindigt gegarandeerd alleen als de grootte van de ge-iterateerde verzameling begrensd blijft tot een gegeven maximum grootte, anders kan het itereren van een verzameling die altijd groeit ertoe leiden dat SCAN nooit een volledige iteratie beëindigt.
Dit is intuïtief eenvoudig te zien: als de verzameling groeit, moet er steeds meer werk worden verricht om alle mogelijke elementen te bezoeken, en de mogelijkheid om de iteratie te beëindigen hangt af van het aantal aanroepen van SCAN en de waarde van de COUNT-optie ten opzichte van de snelheid waarmee de verzameling groeit.
*Waarom kan SCAN alle elementen van een geaggregeerd gegevenstype in één keer retourneren?
In de COUNT
optiedocumentatie staat dat deze familie van commando’s soms alle elementen van een Set, Hash of Sorted Set in één keer kan retourneren in één enkele oproep, ongeacht de COUNT
optiewaarde. De reden waarom dit gebeurt is dat de cursor-gebaseerde iterator alleen kan worden geïmplementeerd, en nuttig is, wanneer het geaggregeerde datatype dat we scannen wordt gerepresenteerd als een hash tabel. Redis gebruikt echter een geheugenoptimalisatie waarbij kleine geaggregeerde datatypes, tot ze een bepaald aantal items of een bepaalde max. grootte van enkele elementen bereiken, worden gerepresenteerd met een compacte single-allocation packed encoding. Wanneer dit het geval is, heeft SCAN geen zinvolle cursor om terug te keren, en moet de hele gegevensstructuur in een keer itereren, dus het enige verstandige gedrag dat het heeft is om alles in een aanroep terug te keren.
Zodra de gegevensstructuren echter groter zijn en gepromoveerd worden om echte hashtabellen te gebruiken, zal de SCAN familie van commando’s terugvallen op het normale gedrag. Merk op dat, omdat dit speciale gedrag van het teruggeven van alle elementen alleen geldt voor kleine aggregaten, het geen effect heeft op de complexiteit van het commando of de latency. De exacte limieten voor de conversie naar echte hashtabellen zijn echter door de gebruiker in te stellen, dus het maximum aantal elementen dat in een enkele aanroep kan worden teruggegeven hangt af van hoe groot het aggregaattype kan zijn en toch de ingepakte representatie kan gebruiken.
Ook moet worden opgemerkt dat dit gedrag specifiek is voor SSCAN, HSCAN en ZSCAN. 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"