K dispozici od verze 2.8.0.
Časová náročnost: O(1) pro každé volání. O(N) pro kompletní iteraci, včetně dostatečného počtu volání příkazu, aby se kurzor vrátil zpět na 0. N je počet prvků uvnitř kolekce.
Příkaz SCAN a úzce související příkazy SSCAN, HSCAN a ZSCAN se používají za účelem inkrementálního iterování nad kolekcí prvků.
- SCAN iteruje množinu klíčů v aktuálně vybrané databázi Redis.
- SSCAN iteruje prvky typů Sets.
- HSCAN iteruje pole typů Hash a jejich přiřazené hodnoty.
- ZSCAN iteruje prvky typů Sorted Set a jejich přiřazené skóre.
Protože tyto příkazy umožňují inkrementální iteraci a vracejí pouze malý počet prvků na jedno volání, lze je používat v produkci bez nevýhod příkazů jako KEYS nebo SMEMBERS, které mohou při volání proti velkým kolekcím klíčů nebo prvků blokovat server po dlouhou dobu (i několik sekund).
Zatímco však blokující příkazy jako SMEMBERS jsou schopny poskytnout všechny prvky, které jsou v daném okamžiku součástí množiny, rodina příkazů SCAN nabízí pouze omezené záruky ohledně vrácených prvků, protože kolekce, kterou inkrementálně iterujeme, se může během iteračního procesu měnit.
Všimněte si, že příkazy SCAN, SSCAN, HSCAN a ZSCAN pracují velmi podobně, takže tato dokumentace pokrývá všechny čtyři příkazy. Zřejmým rozdílem však je, že v případě příkazů SSCAN, HSCAN a ZSCAN je prvním argumentem název klíče, který obsahuje hodnotu množiny, hashe nebo setříděné množiny. Příkaz SCAN žádný argument se jménem klíče nepotřebuje, protože iteruje klíče v aktuální databázi, takže iterovaným objektem je samotná databáze.
- *Základní použití příkazu SCAN
- *Garance SCAN
- *Počet prvků vrácených při každém volání SCAN
- *Volba COUNT
- *Volba MATCH
- *Volba TYPE
- *Vícenásobné paralelní iterace
- *Ukončení iterace v polovině
- *Volání SCAN s poškozeným kurzorem
- *Záruka ukončení
- *Proč může SCAN vrátit všechny prvky agregovaného datového typu v jediném volání?
- *Return value
- *History
- *Additional examples
*Základní použití příkazu SCAN
SCAN je iterátor založený na kurzoru. To znamená, že při každém volání příkazu vrátí server aktualizovaný kurzor, který musí uživatel použít jako argument kurzoru při dalším volání.
Iterace začíná, když je kurzor nastaven na 0, a končí, když je kurzor vrácený serverem roven 0. Následuje příklad iterace 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"
V uvedeném příkladu se při prvním volání použije jako kurzor nula, čímž se iterace zahájí. Druhé volání použije jako první prvek odpovědi kurzor vrácený předchozím voláním, tedy 17.
Jak vidíte, návratová hodnota SCAN je pole dvou hodnot: první hodnota je nový kurzor, který se použije v dalším volání, druhá hodnota je pole prvků.
Protože ve druhém volání je vrácený kurzor 0, server volajícímu signalizuje, že iterace skončila a kolekce byla zcela prozkoumána. Zahájení iterace s hodnotou kurzoru 0 a volání SCAN, dokud není vrácený kurzor opět 0, se nazývá úplná iterace.
*Garance SCAN
Příkaz SCAN a ostatní příkazy z rodiny SCAN jsou schopny poskytnout uživateli sadu garancí spojených s úplnými iteracemi.
- Plná iterace vždy načte všechny prvky, které se v kolekci vyskytovaly od začátku do konce úplné iterace. To znamená, že pokud je daný prvek uvnitř kolekce při zahájení iterace a je tam stále i při ukončení iterace, pak jej v určitém okamžiku SCAN uživateli vrátil.
- Plná iterace nikdy nevrátí žádný prvek, který NEBYL v kolekci přítomen od začátku do konce plné iterace. Pokud byl tedy prvek odstraněn před začátkem iterace a po celou dobu trvání iterace nebyl do kolekce nikdy přidán zpět, SCAN zajistí, že tento prvek nebude nikdy vrácen.
Jelikož však SCAN má velmi málo přidruženého stavu (pouze kurzor), má následující nevýhody:
- Daný prvek může být vrácen vícekrát. Je na aplikaci, aby se s případem duplicitních prvků vypořádala, například aby vrácené prvky použila pouze k provedení operací, které jsou při opakovaném použití bezpečné.
- Prvky, které se v kolekci během úplné iterace neustále nevyskytovaly, mohou být vráceny nebo ne: není to definováno.
*Počet prvků vrácených při každém volání SCAN
Funkce rodiny SCAN nezaručují, že počet prvků vrácených při každém volání je v daném rozsahu. Příkazy mohou vracet i nulové prvky a klient by neměl považovat iteraci za ukončenou, dokud vrácený kurzor není nulový.
Jakkoli je počet vrácených prvků rozumný, to znamená, že z praktického hlediska může SCAN při iteraci velké kolekce vrátit maximálně počet prvků v řádu několika desítek prvků nebo může vrátit všechny prvky kolekce při jediném volání, pokud je iterovaná kolekce dostatečně malá na to, aby mohla být interně reprezentována jako kódovaná datová struktura (to se stává u malých množin, hashů a setříděných množin).
Však existuje způsob, jak může uživatel vyladit řádovou velikost počtu vrácených prvků na jedno volání pomocí volby COUNT.
*Volba COUNT
Ačkoli SCAN neposkytuje záruky ohledně počtu prvků vrácených při každé iteraci, je možné empiricky upravit chování SCAN pomocí volby COUNT. V podstatě pomocí COUNT uživatel určil množství práce, které má být při každém volání vykonáno za účelem získání prvků z kolekce. Jedná se pouze o nápovědu pro implementaci, nicméně obecně lze od implementace očekávat většinou právě toto.
- Výchozí hodnota COUNT je 10.
- Při iteraci prostoru klíčů nebo množiny, hashe nebo setříděné množiny, která je dostatečně velká, aby mohla být reprezentována hashovou tabulkou, za předpokladu, že není použita volba MATCH, server obvykle vrátí počet nebo o něco více než počet prvků na jedno volání. Podívejte se prosím do části Proč může SCAN vrátit všechny prvky najednou, která se nachází dále v tomto dokumentu.
- Při iteraci Sets kódovaných jako intsets (malé množiny složené pouze z celých čísel) nebo Hashes a Sorted Sets kódovaných jako ziplists (malé hashe a množiny složené z malých jednotlivých hodnot) jsou obvykle všechny prvky vráceny při prvním volání SCAN bez ohledu na hodnotu COUNT.
Důležité: není nutné používat stejnou hodnotu COUNT pro každou iteraci. Volající může libovolně měnit počet z jedné iterace na druhou podle potřeby, pokud je kurzor předaný v dalším volání shodný s kurzorem získaným v předchozím volání příkazu.
*Volba MATCH
Je možné iterovat pouze prvky odpovídající danému vzoru ve stylu glob, podobně jako se chová příkaz KEYS, který bere vzor jako jediný argument.
Chcete-li tak učinit, stačí na konec příkazu SCAN připojit MATCH <pattern>
argumenty (funguje se všemi příkazy rodiny SCAN).
Toto je příklad iterace pomocí 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>
Je důležité si uvědomit, že filtr MATCH se použije po načtení prvků z kolekce, těsně před vrácením dat klientovi. To znamená, že pokud vzor odpovídá velmi malému počtu prvků uvnitř kolekce, SCAN pravděpodobně ve většině iterací nevrátí žádné prvky. Příklad je uveden níže:
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>
Jak vidíte, většina volání vrátila nulové prvky, ale poslední volání, kde byl použit COUNT 1000, aby byl příkaz donucen provést více skenování pro tuto iteraci.
*Volba TYPE
Od verze 6.0 můžete pomocí této volby požádat SCAN, aby vrátil pouze objekty, které odpovídají danému type
, což vám umožní iterovat databází a hledat klíče určitého typu. Volba TYPE je k dispozici pouze u SCAN celé databáze, nikoli HSCAN nebo ZSCAN atd.
Argument type
je stejný název řetězce, který vrací příkaz TYPE. Všimněte si zvláštnosti, kdy některé typy Redis, například GeoHashes, HyperLogLogs, Bitmaps a Bitfields, mohou být interně implementovány pomocí jiných typů Redis, například string nebo zset, takže je nelze pomocí SCAN odlišit od jiných klíčů stejného typu. Například ZSET a 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"
Je důležité si uvědomit, že filtr TYPE se použije i po načtení prvků z databáze, takže tato možnost nesnižuje množství práce, kterou musí server vykonat, aby dokončil celou iteraci, a u vzácných typů můžete v mnoha iteracích neobdržet žádné prvky.
*Vícenásobné paralelní iterace
Je možné, aby stejnou kolekci iterovalo nekonečné množství klientů současně, protože úplný stav iterátoru je v kurzoru, který je získán a vrácen klientovi při každém volání. Na straně serveru se nepřebírá vůbec žádný stav.
*Ukončení iterace v polovině
Protože na straně serveru není žádný stav, ale úplný stav je zachycen kurzorem, může volající volně ukončit iteraci v polovině, aniž by to jakkoli signalizoval serveru. Bez problémů lze zahájit nekonečný počet iterací a nikdy je neukončit.
*Volání SCAN s poškozeným kurzorem
Volání SCAN s poškozeným, záporným, mimo rozsah nebo jinak neplatným kurzorem povede k nedefinovanému chování, ale nikdy k pádu. Co bude nedefinované, je to, že implementace SCAN již nemůže zajistit záruky ohledně vrácených prvků.
Jediné platné kurzory, které lze použít, jsou:
- Kurzor s hodnotou 0 při zahájení iterace.
- Kurzor vrácený předchozím voláním SCAN, aby bylo možné pokračovat v iteraci.
*Záruka ukončení
Algoritmus SCAN zaručuje ukončení pouze v případě, že velikost iterované kolekce zůstane omezena na danou maximální velikost, jinak může iterace kolekce, která vždy roste, vést k tomu, že SCAN nikdy neukončí celou iteraci.
To je snadno intuitivně pochopitelné: pokud kolekce roste, je třeba vykonat stále více práce, aby byly navštíveny všechny možné prvky, a schopnost ukončit iteraci závisí na počtu volání SCAN a hodnotě jeho volby COUNT v porovnání s rychlostí, kterou kolekce roste.
*Proč může SCAN vrátit všechny prvky agregovaného datového typu v jediném volání?
V dokumentaci COUNT
volby uvádíme, že někdy může tato rodina příkazů vrátit všechny prvky množiny, hashe nebo setříděné množiny najednou v jediném volání bez ohledu na hodnotu COUNT
volby. Důvodem, proč k tomu dochází, je skutečnost, že iterátor založený na kurzoru lze implementovat a je užitečný pouze v případě, že souhrnný datový typ, který prohledáváme, je reprezentován jako hashovací tabulka. Redis však používá paměťovou optimalizaci, při níž jsou malé agregované datové typy, dokud nedosáhnou daného množství položek nebo dané maximální velikosti jednotlivých prvků, reprezentovány pomocí kompaktního jednoalokovaného paketového kódování. V takovém případě nemá SCAN žádný smysluplný kurzor, který by mohl vrátit, a musí iterovat celou datovou strukturu najednou, takže jediné rozumné chování, které má, je vrátit vše v jednom volání.
Jakmile jsou však datové struktury větší a jsou povýšeny na použití skutečných hešových tabulek, rodina příkazů SCAN se uchýlí k normálnímu chování. Všimněte si, že vzhledem k tomu, že toto speciální chování spočívající ve vracení všech prvků platí pouze pro malé agregáty, nemá žádný vliv na složitost nebo zpoždění příkazu. Přesné limity pro převod do skutečných hašovacích tabulek jsou však konfigurovatelné uživatelem, takže maximální počet prvků, které můžete vidět vrácené v jednom volání, závisí na tom, jak velký může být datový typ agregátu a stále používat balenou reprezentaci.
Také upozorňujeme, že toto chování je specifické pro příkazy SSCAN, HSCAN a 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"