A 2.8.0.
Az időkomplexitás: O(1) minden hívásnál. O(N) egy teljes iterációhoz, beleértve elég parancshívást ahhoz, hogy a kurzor visszatérjen 0-ra. N a gyűjteményen belüli elemek száma.
A SCAN parancs és a vele szorosan összefüggő SSCAN, HSCAN és ZSCAN parancsok arra szolgálnak, hogy inkrementálisan iteráljunk egy elemgyűjteményen.
- A SCAN az aktuálisan kiválasztott Redis adatbázisban lévő kulcsok halmazát iterálja.
- Az SSCAN a Sets típusok elemeit iterálja.
- A HSCAN a Hash típusok mezőit és a hozzájuk tartozó értékeket iterálja.
- A ZSCAN a Sorted Set típusok elemeit és a hozzájuk tartozó értékeket iterálja.
Mivel ezek a parancsok lehetővé teszik az inkrementális iterációt, és hívásonként csak kis számú elemet adnak vissza, a termelésben is használhatók az olyan parancsok hátrányai nélkül, mint a KEYS vagy a SMEMBERS, amelyek hosszú időre (akár több másodpercre) blokkolhatják a szervert, ha nagy kulcs- vagy elemgyűjtemények ellen hívják őket.
Míg azonban az olyan blokkoló parancsok, mint a SMEMBERS képesek egy adott pillanatban a halmaz részét képező összes elemet megadni, addig a SCAN parancscsalád csak korlátozott garanciát nyújt a visszaadott elemekre, mivel a gyűjtemény, amelyet inkrementálisan iterálunk, változhat az iterációs folyamat során.
Megjegyezzük, hogy a SCAN, SSCAN, HSCAN és ZSCAN mind nagyon hasonlóan működik, így ez a dokumentáció mind a négy parancsra kiterjed. Nyilvánvaló különbség azonban, hogy az SSCAN, HSCAN és ZSCAN esetében az első argumentum a Set, Hash vagy Sorted Set értéket tartalmazó kulcs neve. A SCAN parancsnak nincs szüksége kulcsnév argumentumra, mivel az aktuális adatbázisban lévő kulcsokat iterálja, így az iterált objektum maga az adatbázis.
- *SCAN alapvető használata
- *Scan garanciák
- *A minden SCAN hívásnál visszaadott elemek száma
- *A COUNT opció
- *A MATCH opció
- *A TYPE opció
- *Sokszoros párhuzamos iteráció
- *Iterációk félúton történő befejezése
- *A SCAN hívása sérült kurzorral
- *A befejezés garantálása
- *Miért adhatja vissza a SCAN egyetlen hívással egy összesített adattípus összes elemét?
- *Return value
- *History
- *Additional examples
*SCAN alapvető használata
A SCAN egy kurzor alapú iterátor. Ez azt jelenti, hogy a parancs minden egyes hívásakor a kiszolgáló egy frissített kurzort küld vissza, amelyet a felhasználónak a következő híváskor kurzor argumentumként kell használnia.
Az iteráció akkor kezdődik, amikor a kurzor értéke 0, és akkor ér véget, amikor a kiszolgáló által visszaadott kurzor értéke 0. Az alábbiakban egy példa a SCAN iterációra:
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"
A fenti példában az első hívás nullát használ kurzorként az iteráció indításához. A második hívás az előző hívás által visszaadott kurzort használja a válasz első elemeként, azaz 17-et.
Amint látható, a SCAN visszatérési értéke egy két értékből álló tömb: az első érték a következő hívásban használandó új kurzor, a második érték pedig egy elemtömb.
Mivel a második hívásban a visszaadott kurzor 0, a szerver jelezte a hívónak, hogy az iteráció befejeződött, és a gyűjtemény teljesen feltárásra került. Ha egy iterációt 0 kurzorértékkel kezdünk, és addig hívjuk a SCAN parancsot, amíg a visszaadott kurzor értéke ismét 0 nem lesz, azt teljes iterációnak nevezzük.
*Scan garanciák
A SCAN parancs és a SCAN család többi parancsa képes a felhasználó számára a teljes iterációkhoz kapcsolódó garanciákat nyújtani.
- A teljes iteráció mindig visszanyeri az összes olyan elemet, amely a gyűjteményben a teljes iteráció kezdetétől a végéig jelen volt. Ez azt jelenti, hogy ha egy adott elem az iteráció indulásakor a gyűjteményben van, és az iteráció befejezésekor még mindig ott van, akkor a SCAN valamikor visszaadta a felhasználónak.
- A teljes iteráció soha nem ad vissza olyan elemet, amely NEM volt jelen a gyűjteményben a teljes iteráció kezdetétől a végéig. Tehát ha egy elemet az iteráció kezdete előtt eltávolítottunk, és az iteráció teljes időtartama alatt soha nem kerül vissza a gyűjteménybe, a SCAN biztosítja, hogy ez az elem soha nem kerül vissza.
Mivel azonban a SCAN-hez nagyon kevés állapot tartozik (csak a kurzor), a következő hátrányai vannak:
- Egy adott elemet többször is vissza lehet adni. Az alkalmazáson múlik, hogyan kezeli a duplikált elemek esetét, például csak a visszaadott elemeket használja fel olyan műveletek elvégzéséhez, amelyek többszöri ismételt alkalmazás esetén is biztonságosak.
- A teljes iteráció során a gyűjteményben nem folyamatosan jelenlévő elemek visszaadhatók vagy nem: ez nem meghatározott.
*A minden SCAN hívásnál visszaadott elemek száma
A SCAN család függvényei nem garantálják, hogy a hívásonként visszaadott elemek száma egy adott tartományban van. A parancsok nulla elemet is visszaadhatnak, és a kliensnek nem kell az iterációt befejezettnek tekintenie, amíg a visszaadott kurzor nem nulla.
Mindenesetre a visszaadott elemek száma ésszerű, azaz a gyakorlatban a SCAN egy nagy gyűjtemény iterálásakor maximum néhány tízes nagyságrendű elemszámot adhat vissza, vagy egyetlen hívással visszaadhatja a gyűjtemény összes elemét, ha az iterált gyűjtemény elég kicsi ahhoz, hogy belsőleg kódolt adatszerkezetként ábrázolható legyen (ez történik kis halmazok, hashek és rendezett halmazok esetén).
A felhasználónak azonban van módja arra, hogy a COUNT opció segítségével beállítsa a hívásonként visszaadott elemek számának nagyságrendjét.
*A COUNT opció
Míg a SCAN nem nyújt garanciát az egyes iterációk során visszaadott elemek számára, a COUNT opció segítségével empirikusan beállítható a SCAN viselkedése. Alapvetően a COUNT segítségével a felhasználó megadja, hogy minden egyes hívásnál mennyi munkát kell elvégezni annak érdekében, hogy a gyűjteményből elemeket nyerjen ki. Ez csak egy támpont az implementáció számára, azonban általánosságban ez az, amit a legtöbbször elvárhatunk az implementációtól.
- A COUNT alapértelmezett értéke 10.
- A kulcstér vagy egy olyan Set, Hash vagy Sorted Set iterálásakor, amely elég nagy ahhoz, hogy egy hash-táblával reprezentálható legyen, feltéve, hogy nem használunk MATCH opciót, a szerver általában count vagy egy kicsit több mint count elemet fog visszaadni hívásonként. Kérjük, olvassa el a dokumentum későbbi részében a Miért adhatja vissza a SCAN az összes elemet egyszerre című részt.
- Az intsetként kódolt Sets (csak egész számokból álló kis halmazok) vagy a ziplistaként kódolt Hash-ek és Sorted Sets (kis hash-ek és kis egyedi értékekből álló halmazok) iterálásakor általában a COUNT értéktől függetlenül az összes elemet az első SCAN hívás során adja vissza.
Fontos: nem szükséges minden iterációnál ugyanazt a COUNT értéket használni. A hívó szabadon megváltoztathatja a számolást egyik iterációról a másikra, ha a következő híváskor átadott kurzor megegyezik a parancs előző hívásakor kapott értékkel.
*A MATCH opció
Megoldható, hogy csak az adott glob-stílusú mintának megfelelő elemeket iteráljuk, hasonlóan a KEYS parancs viselkedéséhez, amely egyetlen argumentumként egy mintát fogad el.
Ezért csak csatolja a MATCH <pattern>
argumentumokat a SCAN parancs végére (ez a SCAN család összes parancsával működik).
Ez egy példa az iterációra a MATCH használatával:
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>
Fontos megjegyezni, hogy a MATCH szűrő az elemek gyűjteményből való kinyerése után kerül alkalmazásra, közvetlenül az adatok ügyfélnek való visszaküldése előtt. Ez azt jelenti, hogy ha a minta nagyon kevés elemmel egyezik a gyűjteményen belül, a SCAN valószínűleg a legtöbb iterációban nem fog elemeket visszaadni. Egy példa az alábbiakban látható:
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>
Amint látható, a legtöbb hívás nulla elemet adott vissza, de az utolsó hívás, ahol egy 1000-es COUNT értéket használtunk, arra kényszerítette a parancsot, hogy több keresést végezzen az adott iterációban.
*A TYPE opció
A 6.0-s verzió óta használhatja ezt az opciót, hogy megkérje a SCAN-t, hogy csak olyan objektumokat adjon vissza, amelyek megfelelnek egy adott type
típusnak, lehetővé téve, hogy egy adott típusú kulcsok után kutatva végigjárja az adatbázist. A TYPE opció csak a teljes adatbázis SCAN-jánál érhető el, a HSCAN vagy ZSCAN stb. nem.
A type
argumentum ugyanaz a string név, mint amit a TYPE parancs visszaad. Vegyünk észre egy furcsaságot, ahol egyes Redis-típusok, mint például a GeoHashes, HyperLogLogs, Bitmaps és Bitfields, belsőleg más Redis-típusok, például string vagy zset használatával valósulhatnak meg, így a SCAN nem tudja megkülönböztetni őket az azonos típusú kulcsoktól. Például egy ZSET és egy 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"
Fontos megjegyezni, hogy a TYPE szűrő is az elemek adatbázisból való kinyerése után kerül alkalmazásra, így az opció nem csökkenti a szervernek egy teljes iteráció elvégzéséhez szükséges munka mennyiségét, és ritka típusok esetében előfordulhat, hogy sok iteráció során nem kapunk elemeket.
*Sokszoros párhuzamos iteráció
Egyidejűleg végtelen számú kliens is iterálhatja ugyanazt a gyűjteményt, mivel az iterátor teljes állapota a kurzorban van, amelyet minden híváskor megkap és visszaküld a kliensnek. Szerveroldalon egyáltalán nem veszünk fel állapotot.
*Iterációk félúton történő befejezése
Mivel szerveroldalon nincs állapot, de a teljes állapotot a kurzor rögzíti, a hívó szabadon befejezhet egy iterációt félúton anélkül, hogy ezt bármilyen módon jelezné a szervernek. Végtelen számú iteráció indítható és soha nem fejezhető be minden probléma nélkül.
*A SCAN hívása sérült kurzorral
A SCAN hívása sérült, negatív, tartományon kívüli vagy más módon érvénytelen kurzorral nem meghatározott viselkedést, de soha nem összeomlást eredményez. Ami nem lesz definiálatlan, az az, hogy a visszaadott elemekre vonatkozó garanciákat a SCAN implementáció már nem tudja biztosítani.
Az egyetlen érvényes kurzor, amit használhatunk:
- A kurzor értéke 0 az iteráció indításakor.
- A SCAN előző hívása által az iteráció folytatásához visszaadott kurzor.
*A befejezés garantálása
A SCAN algoritmus garantáltan csak akkor fejeződik be, ha az iterált gyűjtemény mérete egy adott maximális méretre korlátozva marad, különben egy mindig növekvő gyűjtemény iterálása azt eredményezheti, hogy a SCAN soha nem fejezi be a teljes iterációt.
Ez intuitívan könnyen belátható: ha a gyűjtemény nő, egyre több és több munkát kell elvégezni ahhoz, hogy az összes lehetséges elemet meglátogassuk, és az iteráció befejezésének képessége a SCAN hívásainak számától és a COUNT opció értékétől függ a gyűjtemény növekedési sebességéhez képest.
*Miért adhatja vissza a SCAN egyetlen hívással egy összesített adattípus összes elemét?
A COUNT
opció dokumentációjában kijelentjük, hogy néha ez a parancscsalád egy Set, Hash vagy Sorted Set összes elemét egyszerre egyetlen hívással visszaadhatja, függetlenül a COUNT
opció értékétől. Ennek az az oka, hogy a kurzor-alapú iterátor csak akkor valósítható meg, és csak akkor hasznos, ha az általunk vizsgált összesített adattípus hash-táblaként van ábrázolva. A Redis azonban egy olyan memóriaoptimalizálást használ, ahol a kis aggregált adattípusok, amíg el nem érnek egy adott elemszámot vagy az egyes elemek adott maximális méretét, egy kompakt, egy allokációval csomagolt kódolással reprezentálódnak. Amikor ez a helyzet, a SCAN-nak nincs értelmes kurzora, amit visszaadhatna, és egyszerre kell végigjárnia az egész adatstruktúrát, így az egyetlen épeszű viselkedése az, hogy mindent visszaad egy hívásban.
Mihelyt azonban az adatstruktúrák nagyobbak lesznek, és valódi hash-táblák használatára léptek elő, a SCAN parancscsalád a normál viselkedéshez folyamodik. Megjegyzendő, hogy mivel ez a speciális viselkedés, miszerint az összes elemet visszaadja, csak a kis aggregátumok esetében igaz, nincs hatása a parancsok bonyolultságára vagy késleltetésére. A valódi hash-táblákká alakítandó pontos határok azonban a felhasználó által konfigurálhatók, így az egyetlen hívás során visszakapott elemek maximális száma attól függ, hogy mekkora lehet egy aggregátum adattípusa, és még mindig a csomagolt reprezentációt használja.
Megjegyzendő még, hogy ez a viselkedés az SSCAN, a HSCAN és a ZSCAN sajátossága. 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"