SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

Tillgänglig sedan 2.8.0.

Tidskomplexitet: O(1) för varje anrop. O(N) för en fullständig iteration, inklusive tillräckligt många kommandokallelser för att markören ska återgå till 0. N är antalet element i samlingen.

Kommandot SCAN och de närbesläktade kommandona SSCAN, HSCAN och ZSCAN används för att stegvis iterera över en samling av element.

  • SCAN itererar mängden nycklar i den för närvarande valda Redis-databasen.
  • SSCAN itererar element av typerna Sets.
  • HSCAN itererar fält av typerna Hash och deras tillhörande värden.
  • ZSCAN itererar element av typerna Sorted Set och deras tillhörande poäng.

Då dessa kommandon tillåter inkrementell iteration och endast returnerar ett litet antal element per anrop, kan de användas i produktion utan nackdelen med kommandon som KEYS eller SMEMBERS som kan blockera servern under lång tid (även flera sekunder) när de anropas mot stora samlingar av nycklar eller element.

Men medan blockerande kommandon som SMEMBERS kan tillhandahålla alla element som ingår i en uppsättning vid ett givet tillfälle, erbjuder SCAN-familjen av kommandon endast begränsade garantier för de returnerade elementen eftersom samlingen som vi itererar inkrementellt kan förändras under iterationsprocessen.

Notera att SCAN, SSCAN, HSCAN och ZSCAN fungerar på ett mycket likartat sätt, så den här dokumentationen täcker alla de fyra kommandona. En uppenbar skillnad är dock att när det gäller SSCAN, HSCAN och ZSCAN är det första argumentet namnet på den nyckel som innehåller värdet för Set, Hash eller Sorted Set. Kommandot SCAN behöver inget argument för nyckelnamnet eftersom det itererar nycklar i den aktuella databasen, så det itererade objektet är själva databasen.

*SCAN basic use

SCAN är en markörbaserad iterator. Det innebär att servern vid varje anrop av kommandot returnerar en uppdaterad markör som användaren måste använda som markörargument i nästa anrop.

En iteration startar när markören sätts till 0 och avslutas när markören som returneras av servern är 0. Nedan följer ett exempel på 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" 

I exemplet ovan använder det första anropet noll som markör, för att starta iterationen. Det andra anropet använder den markör som returneras av det föregående anropet som det första elementet i svaret, det vill säga 17.

Som du kan se är SCAN-returvärdet en matris med två värden: det första värdet är den nya markören som ska användas i nästa anrop, det andra värdet är en matris med element.

Då den returnerade markören i det andra anropet är 0, signalerade servern till anroparen att iterationen är avslutad och att samlingen har utforskats fullständigt. Att starta en iteration med ett markörvärde på 0 och anropa SCAN tills den returnerade markören är 0 igen kallas för en fullständig iteration.

*Scan-garantier

Kommandot SCAN, och de andra kommandona i SCAN-familjen, kan ge användaren en uppsättning garantier som är förknippade med fullständiga iterationer.

  • En fullständig iteration hämtar alltid alla de element som fanns i samlingen från början till slutet av en fullständig iteration. Detta innebär att om ett visst element finns i samlingen när en iteration inleds och fortfarande finns kvar när iterationen avslutas, har SCAN vid någon tidpunkt returnerat det till användaren.
  • En fullständig iteration returnerar aldrig något element som INTE fanns i samlingen från början till slutet av en fullständig iteration. Så om ett element togs bort före starten av en iteration och aldrig läggs tillbaka till samlingen under hela den tid som en iteration varar, säkerställer SCAN att detta element aldrig kommer att returneras.

Men eftersom SCAN har mycket lite tillstånd kopplat till sig (bara markören) har den följande nackdelar:

  • Ett givet element kan returneras flera gånger. Det är upp till applikationen att hantera fallet med duplicerade element, till exempel genom att endast använda de returnerade elementen för att utföra operationer som är säkra när de tillämpas på nytt flera gånger.
  • Element som inte var ständigt närvarande i samlingen under en fullständig iteration kan returneras eller inte: det är odefinierat.

*Antal element som returneras vid varje SCAN-anrop

Funktioner i SCAN-familjen garanterar inte att antalet element som returneras per anrop ligger inom ett visst intervall. Det är också tillåtet för kommandona att returnera noll element, och klienten bör inte betrakta iterationen som avslutad så länge den returnerade markören inte är noll.

Hur som helst är antalet returnerade element rimligt, det vill säga i praktiken kan SCAN returnera ett maximalt antal element i storleksordningen några tiotals element vid iterering av en stor samling, eller returnera alla element i samlingen i ett enda anrop när den itererade samlingen är tillräckligt liten för att internt representeras som en kodad datastruktur (detta sker för små mängder, hashes och sorterade mängder).

Det finns dock ett sätt för användaren att ställa in storleksordningen på antalet returnerade element per anrop med hjälp av COUNT-alternativet.

*The COUNT-alternativet

Sedan SCAN ger inga garantier för antalet element som returneras vid varje iteration är det möjligt att empiriskt justera SCAN:s beteende med hjälp av COUNT-alternativet. Med COUNT anger användaren i princip hur mycket arbete som ska utföras vid varje anrop för att hämta element från samlingen. Detta är bara ett tips för implementeringen, men generellt sett är detta vad du kan förvänta dig för det mesta från implementeringen.

  • Standardvärdet för COUNT är 10.
  • När man itererar nyckelutrymmet eller en Set, Hash eller Sorted Set som är tillräckligt stor för att representeras av en hashtabell, förutsatt att inget MATCH-alternativ används, kommer servern vanligtvis att returnera count eller lite mer än count element per anrop. Se avsnittet varför SCAN kan returnera alla element på en gång senare i detta dokument.
  • När man itererar Sets som är kodade som intsets (små uppsättningar som bara består av heltal), eller Hashes och Sorted Sets som är kodade som ziplistor (små hashes och uppsättningar som består av små individuella värden) returneras vanligen alla element vid det första SCAN-anropet, oavsett COUNT-värdet.

Viktigt: det finns inget behov av att använda samma COUNT-värde för varje iteration. Den som anropar är fri att ändra räknevärdet från en iteration till en annan efter behov, så länge som den markör som lämnas vid nästa anrop är den som erhölls vid det föregående anropet av kommandot.

*MATCH-alternativet

Det är möjligt att endast iterera element som matchar ett givet glob-liknande mönster, på samma sätt som beteendet hos KEYS-kommandot som tar ett mönster som enda argument.

För att göra det är det bara att lägga till MATCH <pattern> argumenten i slutet av SCAN-kommandot (det fungerar med alla kommandon i SCAN-familjen).

Detta är ett exempel på iteration med hjälp av 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> 

Det är viktigt att notera att MATCH-filtret appliceras efter det att elementen hämtats från samlingen, strax innan data returneras till klienten. Detta innebär att om mönstret matchar väldigt få element i samlingen kommer SCAN troligen att returnera inga element i de flesta iterationer. Ett exempel visas nedan:

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> 

Som du kan se returnerade de flesta av anropen noll element, men det sista anropet där ett COUNT på 1000 användes för att tvinga kommandot att göra mer skanning för den iterationen.

*The TYPE option

Från och med version 6.0 kan du använda detta alternativ för att be SCAN att endast returnera objekt som matchar en viss type, så att du kan iterera genom databasen och leta efter nycklar av en viss typ. Alternativet TYPE är endast tillgängligt på SCAN för hela databasen, inte HSCAN eller ZSCAN etc.

Argumentet type är samma strängnamn som TYPE-kommandot returnerar. Observera att vissa Redis-typer, t.ex. GeoHashes, HyperLogLogs, Bitmaps och Bitfields, kan internt implementeras med hjälp av andra Redis-typer, t.ex. strängar eller zset, och kan därför inte särskiljas från andra nycklar av samma typ av SCAN. Till exempel en ZSET och 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" 

Det är viktigt att notera att TYPE-filtret också tillämpas efter det att element hämtas från databasen, så alternativet minskar inte mängden arbete som servern måste göra för att slutföra en fullständig iteration, och för sällsynta typer kan det hända att du inte får några element i många iterationer.

*Multipla parallella iterationer

Det är möjligt för ett oändligt antal klienter att iterera samma samling samtidigt, eftersom iteratorns fullständiga tillstånd finns i markören, som hämtas och returneras till klienten vid varje anrop. På serversidan tas inget tillstånd alls.

*Avsluta iterationer i mitten

Då det inte finns något tillstånd på serversidan, utan det fullständiga tillståndet fångas upp av markören, är det fritt fram för anroparen att avbryta en iteration halvvägs utan att signalera detta till servern på något sätt. Ett oändligt antal iterationer kan startas och aldrig avslutas utan problem.

*Kalla SCAN med en skadad markör

Kalla SCAN med en trasig, negativ, utanför intervallet eller på annat sätt ogiltig markör kommer att resultera i odefinierat beteende men aldrig i en krasch. Vad som kommer att vara odefinierat är att garantierna om de returnerade elementen inte längre kan säkerställas av SCAN-implementationen.

De enda giltiga markörer som kan användas är:

  • Markörvärdet 0 när man startar en iteration.
  • Markören som returnerades av det föregående anropet till SCAN för att fortsätta iterationen.

*Garanti för avslut

Scan-algoritmen är garanterad att avsluta endast om storleken på den itererade samlingen förblir begränsad till en given maximal storlek, annars kan iterering av en samling som alltid växer resultera i att SCAN aldrig avslutar en fullständig iteration.

Detta är lätt att förstå intuitivt: om samlingen växer finns det mer och mer arbete att göra för att besöka alla möjliga element, och förmågan att avsluta iterationen beror på antalet anrop till SCAN och dess COUNT-alternativvärde jämfört med den hastighet med vilken samlingen växer.

*Varför SCAN kan returnera alla element av en aggregerad datatyp i ett enda anrop?

I dokumentationen för COUNT-alternativet anger vi att denna kommandofamilj ibland kan returnera alla element av en Set, Hash eller Sorted Set på en gång i ett enda anrop, oavsett COUNT-alternativets värde. Anledningen till att detta händer är att den markörbaserade iteratorn kan implementeras, och är användbar, endast när den aggregerade datatypen som vi söker igenom representeras som en hashtabell. Redis använder dock en minnesoptimering där små aggregerade datatyper, tills de når ett visst antal objekt eller en viss maxstorlek för enskilda element, representeras med hjälp av en kompakt packad kodning med en enda allokering. När detta är fallet har SCAN ingen meningsfull markör att returnera och måste iterera hela datastrukturen på en gång, så det enda vettiga beteendet är att returnera allt i ett anrop.

När datastrukturerna är större och befordras till att använda riktiga hashtabeller, kommer SCAN-familjen av kommandon att ta till det normala beteendet. Observera att eftersom detta specialbeteende med att returnera alla element endast gäller för små aggregat har det inga effekter på kommandots komplexitet eller latenstid. De exakta gränserna för omvandling till riktiga hashtabeller kan dock konfigureras av användaren, så det maximala antalet element som kan returneras i ett enda anrop beror på hur stor en aggregerad datatyp kan vara och fortfarande använda den packade representationen.

Och notera att detta beteende är specifikt för SSCAN, HSCAN och 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" 

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *