Disponibil începând cu 2.8.0.
Complexitate de timp: O(1) pentru fiecare apel. O(N) pentru o iterație completă, incluzând suficiente apeluri de comandă pentru ca cursorul să se întoarcă înapoi la 0. N este numărul de elemente din interiorul colecției.
Comanda SCAN și comenzile înrudite îndeaproape SSCAN, HSCAN și ZSCAN sunt utilizate pentru a itera incremental peste o colecție de elemente.
- SCAN itersează setul de chei din baza de date Redis selectată în mod curent.
- SSCAN itersează elementele de tip Set.
- HSCAN itersează câmpurile de tip Hash și valorile asociate acestora.
- ZSCAN itersează elementele de tip Sorted Set și scorurile asociate acestora.
Din moment ce aceste comenzi permit o iterație incrementală, returnând doar un număr mic de elemente la fiecare apel, ele pot fi utilizate în producție fără dezavantajele unor comenzi precum KEYS sau SMEMBERS care pot bloca serverul pentru o perioadă lungă de timp (chiar câteva secunde) atunci când sunt apelate împotriva unor colecții mari de chei sau elemente.
Cu toate acestea, în timp ce comenzile care blochează, cum ar fi SMEMBERS, sunt capabile să furnizeze toate elementele care fac parte dintr-un set la un moment dat, familia de comenzi SCAN oferă doar garanții limitate cu privire la elementele returnate, deoarece colecția pe care o iterăm în mod incremental se poate schimba în timpul procesului de iterație.
Rețineți că SCAN, SSCAN, HSCAN și ZSCAN funcționează în mod foarte similar, astfel încât această documentație acoperă toate cele patru comenzi. Cu toate acestea, o diferență evidentă este că, în cazul comenzilor SSCAN, HSCAN și ZSCAN, primul argument este numele cheii care conține valoarea Set, Hash sau Sorted Set. Comanda SCAN nu are nevoie de niciun argument privind numele cheii, deoarece itera cheile din baza de date curentă, astfel încât obiectul iterat este chiar baza de date.
- *SCAN basic usage
- *Garanții SCAN
- *Numărul de elemente returnate la fiecare apel SCAN
- *Opțiunea COUNT
- *Opțiunea MATCH
- *Opțiunea TYPE
- *Iteraționări paralele multiple
- *Terminarea iterațiilor la mijloc
- *Calling SCAN with a corrupted cursor
- *Garantarea terminării
- *De ce SCAN poate returna toate elementele unui tip de date agregate într-un singur apel?
- *Return value
- *History
- *Additional examples
*SCAN basic usage
SCAN este un iterator bazat pe cursor. Aceasta înseamnă că, la fiecare apel al comenzii, serverul returnează un cursor actualizat pe care utilizatorul trebuie să îl folosească ca argument pentru cursor în următorul apel.
O iterație începe atunci când cursorul este setat la 0 și se termină atunci când cursorul returnat de server este 0. Următorul este un exemplu de iterație SCAN:
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"
În exemplul de mai sus, primul apel folosește zero ca cursor, pentru a începe iterația. Al doilea apel utilizează cursorul returnat de apelul anterior ca prim element al răspunsului, adică 17.
După cum puteți vedea, valoarea de returnare SCAN este un array de două valori: prima valoare este noul cursor care va fi utilizat în următorul apel, iar a doua valoare este un array de elemente.
Deoarece în al doilea apel cursorul returnat este 0, serverul a semnalat apelantului că iterația s-a încheiat, iar colecția a fost complet explorată. Începerea unei iterații cu o valoare a cursorului de 0 și apelarea SCAN până când cursorul returnat este din nou 0 se numește iterație completă.
*Garanții SCAN
Comanda SCAN, precum și celelalte comenzi din familia SCAN, sunt capabile să ofere utilizatorului un set de garanții asociate iterațiilor complete.
- O iterație completă recuperează întotdeauna toate elementele care au fost prezente în colecție de la începutul până la sfârșitul unei iterații complete. Acest lucru înseamnă că, dacă un anumit element se află în interiorul colecției atunci când se începe o iterație și este încă acolo atunci când se termină o iterație, atunci la un moment dat SCAN l-a returnat utilizatorului.
- O iterație completă nu returnează niciodată niciun element care NU a fost prezent în colecție de la începutul până la sfârșitul unei iterații complete. Astfel, dacă un element a fost eliminat înainte de începutul unei iterații și nu este niciodată adăugat din nou în colecție pe toată durata unei iterații, SCAN se asigură că acest element nu va fi niciodată returnat.
Totuși, deoarece SCAN are foarte puțină stare asociată (doar cursorul), are următoarele dezavantaje:
- Un anumit element poate fi returnat de mai multe ori. Este la latitudinea aplicației să gestioneze cazul elementelor duplicate, de exemplu, folosind doar elementele returnate pentru a efectua operații care sunt sigure atunci când sunt reaplicate de mai multe ori.
- Elementele care nu au fost prezente în mod constant în colecție în timpul unei iterații complete, pot fi returnate sau nu: este nedefinit.
*Numărul de elemente returnate la fiecare apel SCAN
Funcțiile familiei SCAN nu garantează că numărul de elemente returnate la fiecare apel se află într-un anumit interval. De asemenea, comenzilor li se permite să returneze elemente zero, iar clientul nu trebuie să considere iterația încheiată atâta timp cât cursorul returnat nu este zero.
Cu toate acestea, numărul de elemente returnate este rezonabil, adică, în termeni practici, SCAN poate returna un număr maxim de elemente de ordinul câtorva zeci de elemente la iterația unei colecții mari sau poate returna toate elementele colecției într-un singur apel atunci când colecția iterată este suficient de mică pentru a fi reprezentată intern ca o structură de date codificată (acest lucru se întâmplă pentru seturi mici, hașuri și seturi sortate).
Există însă o modalitate prin care utilizatorul poate regla ordinul de mărime al numărului de elemente returnate la fiecare apel, folosind opțiunea COUNT.
*Opțiunea COUNT
În timp ce SCAN nu oferă garanții cu privire la numărul de elemente returnate la fiecare iterație, este posibil să se ajusteze empiric comportamentul SCAN folosind opțiunea COUNT. Practic, cu COUNT, utilizatorul a specificat cantitatea de muncă care trebuie efectuată la fiecare apel pentru a prelua elemente din colecție. Acesta este doar un indiciu pentru implementare, însă, în general, aceasta este ceea ce v-ați putea aștepta de cele mai multe ori din partea implementării.
- Valoarea implicită COUNT este 10.
- Când iterați spațiul de chei, sau un Set, Hash sau Sorted Set care este suficient de mare pentru a fi reprezentat de un tabel hash, presupunând că nu se utilizează opțiunea MATCH, serverul va returna de obicei count sau puțin mai mult decât count elemente la fiecare apel. Vă rugăm să verificați secțiunea Why SCAN may return all the elements at once (De ce SCAN poate returna toate elementele deodată) mai târziu în acest document.
- Când iterați Seturi codificate ca intsets (seturi mici compuse doar din numere întregi), sau Hashes și Sorted Sets codificate ca ziplists (hashes mici și seturi compuse din valori individuale mici), de obicei toate elementele sunt returnate în primul apel SCAN, indiferent de valoarea COUNT.
Important: nu este necesar să folosiți aceeași valoare COUNT pentru fiecare iterație. Apelantul este liber să schimbe numărătoarea de la o iterație la alta, după cum este necesar, atâta timp cât cursorul transmis în apelul următor este cel obținut în apelul anterior al comenzii.
*Opțiunea MATCH
Este posibil să se itereze numai elementele care se potrivesc cu un anumit model de tip glob, în mod similar comportamentului comenzii KEYS care primește un model ca unic argument.
Pentru a face acest lucru, este suficient să adăugați argumentele MATCH <pattern>
la sfârșitul comenzii SCAN (funcționează cu toate comenzile din familia SCAN).
Acesta este un exemplu de iterație folosind 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>
Este important de reținut că filtrul MATCH este aplicat după ce elementele sunt recuperate din colecție, chiar înainte de a returna datele către client. Acest lucru înseamnă că, dacă modelul se potrivește cu foarte puține elemente din interiorul colecției, SCAN nu va returna probabil niciun element în majoritatea iterațiilor. Un exemplu este prezentat mai jos:
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>
După cum puteți vedea, majoritatea apelurilor au returnat zero elemente, dar ultimul apel în care a fost folosit un COUNT de 1000 pentru a forța comanda să facă mai multe scanări pentru acea iterație.
*Opțiunea TYPE
Începând cu versiunea 6.0, puteți utiliza această opțiune pentru a cere SCAN să returneze numai obiectele care se potrivesc cu un anumit type
, permițându-vă să iterați prin baza de date în căutarea cheilor de un anumit tip. Opțiunea TYPE este disponibilă numai pentru SCAN pentru întreaga bază de date, nu și pentru HSCAN sau ZSCAN etc.
Argumentul type
este același nume de șir de caractere pe care îl returnează comanda TYPE. Rețineți o ciudățenie în care unele tipuri Redis, cum ar fi GeoHashes, HyperLogLogs, Bitmaps și Bitfields, pot fi implementate intern utilizând alte tipuri Redis, cum ar fi un șir de caractere sau zset, astfel încât nu pot fi distinse de alte chei de același tip de către SCAN. De exemplu, un ZSET și un 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"
Este important să rețineți că filtrul TYPE se aplică, de asemenea, după ce elementele sunt recuperate din baza de date, astfel încât opțiunea nu reduce cantitatea de muncă pe care serverul trebuie să o facă pentru a finaliza o iterație completă, iar pentru tipurile rare este posibil să nu primiți niciun element în multe iterații.
*Iteraționări paralele multiple
Este posibil ca un număr infinit de clienți să itereze aceeași colecție în același timp, deoarece starea completă a iteratorului se află în cursor, care este obținut și returnat clientului la fiecare apel. Pe partea serverului nu este preluată nicio stare.
*Terminarea iterațiilor la mijloc
Din moment ce nu există nicio stare pe partea serverului, dar starea completă este capturată de cursor, apelantul este liber să încheie o iterație la jumătatea drumului fără a semnala acest lucru serverului în vreun fel. Un număr infinit de iterații pot fi începute și niciodată terminate fără nicio problemă.
*Calling SCAN with a corrupted cursor
Calling SCAN with a broken, negative, out of range, or otherwise invalid cursor, will result into undefined behavior but never into a crash. Ceea ce va fi nedefinit este faptul că garanțiile cu privire la elementele returnate nu mai pot fi asigurate de către implementarea SCAN.
Singurii cursori valabili care pot fi utilizați sunt:
- Cursorul cu valoarea 0 atunci când se începe o iterație.
- Cursorul returnat de apelul anterior către SCAN pentru a continua iterația.
*Garantarea terminării
Garantarea terminării algoritmului SCAN este garantată numai dacă dimensiunea colecției iterate rămâne limitată la o anumită dimensiune maximă, în caz contrar, iterarea unei colecții care crește mereu poate avea ca rezultat faptul că SCAN nu termină niciodată o iterație completă.
Acest lucru este ușor de observat intuitiv: dacă colecția crește, există din ce în ce mai multă muncă de făcut pentru a vizita toate elementele posibile, iar capacitatea de a termina iterația depinde de numărul de apeluri către SCAN și de valoarea opțiunii COUNT a acestuia în comparație cu rata de creștere a colecției.
*De ce SCAN poate returna toate elementele unui tip de date agregate într-un singur apel?
În documentația opțiunii COUNT
, precizăm că, uneori, această familie de comenzi poate returna toate elementele unui Set, Hash sau Sorted Set deodată într-un singur apel, indiferent de valoarea opțiunii COUNT
. Motivul pentru care se întâmplă acest lucru este că iteratorul bazat pe cursor poate fi implementat, și este util, numai atunci când tipul de date agregate pe care îl scanăm este reprezentat ca un tabel hash. Cu toate acestea, Redis utilizează o optimizare a memoriei în care tipurile de date agregate mici, până când ajung la o anumită cantitate de elemente sau la o anumită dimensiune maximă a elementelor unice, sunt reprezentate utilizând o codificare compactă cu o singură alocare împachetată. În acest caz, SCAN nu are niciun cursor semnificativ pe care să îl returneze și trebuie să itereze întreaga structură de date dintr-o dată, astfel încât singurul comportament sănătos pe care îl are este să returneze totul într-un apel.
Dar, odată ce structurile de date sunt mai mari și sunt promovate pentru a utiliza tabele hash reale, familia de comenzi SCAN va recurge la comportamentul normal. Rețineți că, deoarece acest comportament special de returnare a tuturor elementelor este valabil numai pentru agregate mici, nu are efecte asupra complexității sau latenței comenzilor. Cu toate acestea, limitele exacte pentru a fi convertite în tabele hash reale sunt configurabile de către utilizator, astfel încât numărul maxim de elemente pe care le puteți vedea returnate într-un singur apel depinde de cât de mare ar putea fi un tip de date agregate și să folosească în continuare reprezentarea împachetată.
Rețineți, de asemenea, că acest comportament este specific pentru SSCAN, HSCAN și 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"