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

Dostępne od 2.8.0.

Złożoność czasowa: O(1) dla każdego wywołania. O(N) dla pełnej iteracji, w tym wystarczająca liczba wywołań komend, aby kursor wrócił do 0. N jest liczbą elementów w kolekcji.

Komenda SCAN i ściśle powiązane z nią komendy SSCAN, HSCAN i ZSCAN służą do przyrostowego iterowania po kolekcji elementów.

  • SCAN iteruje zbiór kluczy w aktualnie wybranej bazie danych Redis.
  • SSCAN iteruje elementy typów Sets.
  • HSCAN iteruje pola typów Hash i ich wartości.
  • ZSCAN iteruje elementy typów Sorted Set i ich wyniki.

Ponieważ te polecenia pozwalają na iterację przyrostową, zwracając tylko niewielką liczbę elementów na wywołanie, mogą być używane na produkcji bez minusów poleceń takich jak KEYS czy SMEMBERS, które mogą blokować serwer na długi czas (nawet kilka sekund), gdy są wywoływane przeciwko dużym kolekcjom kluczy lub elementów.

Jednakże, podczas gdy blokujące polecenia takie jak SMEMBERS są w stanie dostarczyć wszystkie elementy, które są częścią zbioru w danym momencie, polecenia z rodziny SCAN oferują tylko ograniczone gwarancje dotyczące zwracanych elementów, ponieważ kolekcja, którą iterujemy przyrostowo może się zmieniać podczas procesu iteracji.

Zauważ, że SCAN, SSCAN, HSCAN i ZSCAN działają bardzo podobnie, więc ta dokumentacja obejmuje wszystkie te cztery polecenia. Jednak oczywistą różnicą jest to, że w przypadku SSCAN, HSCAN i ZSCAN pierwszym argumentem jest nazwa klucza przechowującego wartość Set, Hash lub Sorted Set. Polecenie SCAN nie potrzebuje żadnego argumentu nazwy klucza, ponieważ iteruje klucze w bieżącej bazie danych, więc iterowanym obiektem jest sama baza danych.

*Podstawowe użycie SCAN

SCAN jest iteratorem opartym na kursorze. Oznacza to, że przy każdym wywołaniu polecenia serwer zwraca zaktualizowany kursor, którego użytkownik musi użyć jako argumentu kursora w następnym wywołaniu.

Iteracja rozpoczyna się, gdy kursor jest ustawiony na 0, a kończy się, gdy kursor zwrócony przez serwer ma wartość 0. Poniżej przedstawiono przykład iteracji 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" 

W powyższym przykładzie pierwsze wywołanie używa zera jako kursora, aby rozpocząć iterację. Drugie wywołanie używa kursora zwróconego przez poprzednie wywołanie jako pierwszego elementu odpowiedzi, czyli 17.

Jak widać wartość zwracana przez SCAN jest tablicą dwóch wartości: pierwsza wartość to nowy kursor do użycia w następnym wywołaniu, druga wartość to tablica elementów.

Ponieważ w drugim wywołaniu zwróconym kursorem jest 0, serwer zasygnalizował wywołującemu, że iteracja się zakończyła, a kolekcja została całkowicie zbadana. Rozpoczęcie iteracji z kursorem o wartości 0 i wywoływanie SCAN do momentu, aż zwrócony kursor ponownie będzie miał wartość 0 nazywamy pełną iteracją.

*Gwarancje SCAN

Komenda SCAN, oraz inne komendy z rodziny SCAN, są w stanie zapewnić użytkownikowi zestaw gwarancji związanych z pełnymi iteracjami.

  • Pełna iteracja zawsze pobiera wszystkie elementy, które były obecne w kolekcji od początku do końca pełnej iteracji. Oznacza to, że jeśli dany element znajduje się w kolekcji w momencie rozpoczęcia iteracji i nadal tam jest, gdy iteracja się kończy, to w pewnym momencie SCAN zwrócił go użytkownikowi.
  • Pełna iteracja nigdy nie zwraca żadnego elementu, który NIE był obecny w kolekcji od początku do końca pełnej iteracji. Więc jeśli element został usunięty przed rozpoczęciem iteracji i nigdy nie został dodany z powrotem do kolekcji przez cały czas trwania iteracji, SCAN zapewnia, że ten element nigdy nie zostanie zwrócony.

Jednakże ponieważ SCAN ma bardzo mało powiązanego stanu (tylko kursor), ma następujące wady:

  • Dany element może zostać zwrócony wiele razy. Od aplikacji zależy, jak poradzi sobie z przypadkiem zduplikowanych elementów, na przykład używając zwróconych elementów tylko do wykonania operacji, które są bezpieczne przy wielokrotnym zastosowaniu.
  • Elementy, które nie były stale obecne w kolekcji podczas pełnej iteracji, mogą zostać zwrócone lub nie: jest to niezdefiniowane.

*Liczba elementów zwracanych przy każdym wywołaniu SCAN

Funkcje rodziny SCAN nie gwarantują, że liczba elementów zwracanych przy każdym wywołaniu mieści się w określonym zakresie. Dozwolone jest również zwracanie zerowych elementów, a klient nie powinien uznawać iteracji za zakończoną tak długo, jak długo zwracany kursor nie jest równy zero.

Jakkolwiek liczba zwracanych elementów jest rozsądna, to znaczy w praktyce SCAN może zwracać maksymalną liczbę elementów rzędu kilkudziesięciu elementów przy iteracji dużego zbioru lub może zwracać wszystkie elementy zbioru w jednym wywołaniu, gdy iterowany zbiór jest na tyle mały, że może być wewnętrznie reprezentowany jako zakodowana struktura danych (dzieje się tak dla małych zbiorów, haszy i zbiorów posortowanych).

Jednakże istnieje sposób, aby użytkownik mógł dostosować rząd wielkości liczby zwracanych elementów na jedno wywołanie za pomocą opcji COUNT.

*Opcja COUNT

Pomimo, że SCAN nie daje gwarancji co do liczby elementów zwracanych przy każdej iteracji, możliwe jest empiryczne dostosowanie zachowania SCAN za pomocą opcji COUNT. Zasadniczo za pomocą COUNT użytkownik określa ilość pracy, która powinna zostać wykonana przy każdym wywołaniu w celu pobrania elementów z kolekcji. Jest to tylko wskazówka dla implementacji, jednak ogólnie rzecz biorąc jest to to, czego można się spodziewać po implementacji w większości przypadków.

  • Domyślną wartością COUNT jest 10.
  • Podczas iteracji przestrzeni kluczy lub zbioru, haszu lub posortowanego zbioru, który jest wystarczająco duży, aby być reprezentowany przez tablicę haszującą, zakładając, że nie użyto opcji MATCH, serwer zazwyczaj zwróci count lub nieco więcej niż count elementów na jedno wywołanie. Sprawdź dlaczego SCAN może zwrócić wszystkie elementy na raz w dalszej części tego dokumentu.
  • Przy iteracji zbiorów zakodowanych jako intsety (małe zbiory składające się z samych liczb całkowitych), lub haszy i zbiorów posortowanych zakodowanych jako ziplisty (małe hashe i zbiory składające się z małych pojedynczych wartości), zazwyczaj wszystkie elementy są zwracane w pierwszym wywołaniu SCAN niezależnie od wartości COUNT.

Ważne: nie ma potrzeby używania tej samej wartości COUNT dla każdej iteracji. Wywołujący może dowolnie zmieniać wartość COUNT z jednej iteracji na drugą, o ile kursor przekazywany w następnym wywołaniu jest tym samym, który został uzyskany w poprzednim wywołaniu polecenia.

*Opcja MATCH

Możliwe jest iterowanie tylko po elementach pasujących do podanego wzorca w stylu globali, podobnie jak zachowuje się polecenie KEYS przyjmujące wzorzec jako jedyny argument.

W tym celu wystarczy dodać argumenty MATCH <pattern> na końcu polecenia SCAN (działa to ze wszystkimi poleceniami z rodziny SCAN).

Przykład iteracji z użyciem 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> 

Należy pamiętać, że filtr MATCH jest stosowany po pobraniu elementów z kolekcji, tuż przed zwróceniem danych do klienta. Oznacza to, że jeśli wzorzec pasuje do bardzo małej ilości elementów wewnątrz kolekcji, SCAN prawdopodobnie nie zwróci żadnych elementów w większości iteracji. Przykład pokazany jest poniżej:

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 widać większość wywołań zwróciła zero elementów, ale ostatnie wywołanie, w którym użyto wartości COUNT równej 1000, miało na celu zmuszenie polecenia do wykonania większej ilości skanowania dla tej iteracji.

*Opcja TYPE

Od wersji 6.0 możesz użyć tej opcji, aby poprosić SCAN o zwrócenie tylko tych obiektów, które pasują do danego type, pozwalając na iterację po bazie danych w poszukiwaniu kluczy określonego typu. Opcja TYPE jest dostępna tylko dla SCAN całej bazy danych, nie HSCAN lub ZSCAN itp.

type Argument type jest tą samą nazwą łańcucha, którą zwraca polecenie TYPE. Zwróć uwagę na dziwactwo, gdzie niektóre typy Redis, takie jak GeoHashes, HyperLogLogs, Bitmaps i Bitfields, mogą być wewnętrznie zaimplementowane przy użyciu innych typów Redis, takich jak string lub zset, więc nie mogą być odróżnione od innych kluczy tego samego typu przez SCAN. Na przykład, ZSET i 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" 

Należy pamiętać, że filtr TYPE jest również stosowany po pobraniu elementów z bazy danych, więc opcja ta nie zmniejsza ilości pracy, jaką serwer musi wykonać, aby zakończyć pełną iterację, a dla rzadkich typów możesz nie otrzymać żadnych elementów w wielu iteracjach.

*Wielokrotne równoległe iteracje

Możliwe jest dla nieskończonej liczby klientów iterowanie tej samej kolekcji w tym samym czasie, ponieważ pełny stan iteratora znajduje się w kursorze, który jest uzyskiwany i zwracany do klienta przy każdym wywołaniu. Po stronie serwera żaden stan nie jest pobierany.

*Zakończenie iteracji w środku

Ponieważ nie ma stanu po stronie serwera, ale pełny stan jest przechwytywany przez kursor, dzwoniący może zakończyć iterację w połowie drogi bez sygnalizowania tego serwerowi w jakikolwiek sposób. Nieskończona liczba iteracji może być rozpoczęta i nigdy nie zakończona bez żadnego problemu.

*Wywołanie SCAN z uszkodzonym kursorem

Wywołanie SCAN z uszkodzonym, ujemnym, poza zakresem lub w inny sposób nieprawidłowym kursorem, spowoduje niezdefiniowane zachowanie, ale nigdy nie spowoduje awarii. Niezdefiniowane będzie jedynie to, że gwarancje dotyczące zwracanych elementów nie mogą być dłużej zapewniane przez implementację SCAN.

Jedynymi poprawnymi kursorami do użycia są:

  • Kursor o wartości 0 podczas rozpoczynania iteracji.
  • Kursor zwrócony przez poprzednie wywołanie SCAN w celu kontynuowania iteracji.

*Gwarancja zakończenia

Algorytm SCAN ma gwarancję zakończenia tylko wtedy, gdy rozmiar iterowanej kolekcji pozostaje ograniczony do danego maksymalnego rozmiaru, w przeciwnym razie iterowanie kolekcji, która zawsze rośnie, może spowodować, że SCAN nigdy nie zakończy pełnej iteracji.

Jest to łatwe do zrozumienia intuicyjnie: jeśli kolekcja rośnie, jest coraz więcej pracy do wykonania w celu odwiedzenia wszystkich możliwych elementów, a zdolność do zakończenia iteracji zależy od liczby wywołań SCAN i wartości opcji COUNT w porównaniu z tempem, w jakim rośnie kolekcja.

*Dlaczego SCAN może zwrócić wszystkie elementy zagregowanego typu danych w jednym wywołaniu?

W dokumentacji opcji COUNT stwierdzamy, że czasami ta rodzina poleceń może zwrócić wszystkie elementy zbioru Set, Hash lub Sorted Set jednocześnie w jednym wywołaniu, niezależnie od wartości opcji COUNT. Powodem, dla którego tak się dzieje jest fakt, że iterator oparty na kursorze może być zaimplementowany i jest użyteczny tylko wtedy, gdy zagregowany typ danych, który skanujemy jest reprezentowany jako tablica hash. Jednakże Redis używa optymalizacji pamięci, gdzie małe zagregowane typy danych, dopóki nie osiągną określonej ilości elementów lub określonego maksymalnego rozmiaru pojedynczych elementów, są reprezentowane przy użyciu kompaktowego kodowania spakowanego z pojedynczą alokacją. Gdy tak jest, SCAN nie ma żadnego sensownego kursora do zwrócenia i musi iterować całą strukturę danych na raz, więc jedynym rozsądnym zachowaniem jest zwrócenie wszystkiego w wywołaniu.

Jednakże gdy struktury danych są większe i są promowane do używania prawdziwych tablic hash, rodzina poleceń SCAN ucieka się do normalnego zachowania. Zauważ, że ponieważ to specjalne zachowanie polegające na zwracaniu wszystkich elementów jest prawdziwe tylko dla małych agregatów, nie ma to żadnego wpływu na złożoność polecenia lub opóźnienie. Jednakże dokładne limity, które zostaną zamienione na prawdziwe tablice hashujące są konfigurowalne przez użytkownika, więc maksymalna liczba elementów zwracanych w pojedynczym wywołaniu zależy od tego, jak duży może być typ danych zagregowanych i nadal używać spakowanej reprezentacji.

Zauważ również, że to zachowanie jest specyficzne dla 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" 

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *