Redis i Python – dobrze dobrana para #4

Cześć.

Ostatni wpis wprowadzał nam listy, bazy oraz TTL’a. Dziś postawimy kilka kolejnych kroków, przybliżających nas do poznania Redisa.

Bez przydługich wstępów – zaczynajmy.

Zbiory

Zbiory, podobnie jak w innych rozwiązaniach, to kolekcje unikalnych, nie zachowujących kolejności, elementów. W przypadku Redisa – stringów.

Dwie podstawowe komendy służące do pracy ze zbiorami to SADD i SMEMBERS. Pierwsza z nich dodaje element do zbioru a druga pobiera elementy. Zerknij na kod:

1
2
3
4
5
6
7
8
9
from redis import Redis

redis_connection = Redis(decode_responses=True)

redis_connection.sadd("key", "val1")
redis_connection.sadd("key", "val2")
redis_connection.sadd("key", "val3")

print(redis_connection.smembers("key"))

Który, zgodnie z zasadą działania zbiorów, zwróci za każdym uruchomieniem, elementy w innej kolejności.

Jak łatwo się domyśleć, na zbiorach można wykonywać pewne operacje znane z matematyki np. część wspólna zbiorów, różnica zbiorów itp.

Wspomnianą różnicę zbiorów możemy osiągnąć stosując polecenie SDIFF, część wspólną zbiorów uzyskamy poleceniem SINTER a suma zbiorów to polecenie SUNION.

Gdzie zysk? To co przychodzi mi od razu na myśl to złożoność obliczeniowa tych operacji. Raz że jest dobra a dwa że zawsze jest podana w dokumentacji i np.:

  • SDIFF – O(N) where N is the total number of elements in all given sets.
  • SINTER – O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.
  • SUNION – O(N) where N is the total number of elements in all given sets.

Dostępność tych komend pozwala nam robić operacje na zbiorach bez wyciągania ich z bazy – co oszczędza chociażby pamięć RAM, bo nie musimy trzymać tych elementów w pamięci. Ponadto, operacje typu SDIFF mają warianty (SDIFFSTORE) pozwalając od razu zapisać rezultat pod konkretny klucz, bez konieczności pobierania go do programu.

Posortowane zbiory

Redis udostępnia pewna wariację zbiorów, jaką jest posortowany zbiór, czyli sorted set. W tym typie danych, do każdego elementu zbioru, przypisana jest pewna wartość numeryczna, zwana score, używana do zachowania kolejności. Elementy w zbiorze są posortowane rosnąco względem tej wartości. Na potrzeby wpisu będziemy mówić na tę wartość „waga”.

Siłą tych zbiorów jest, ponownie, złożoność obliczeniowa. Podstawowe polecenia oscyluja w okolicach złożoności liniowej.

Komendą ZADD dodajemy elementy a ZRANGE pobieramy je. Zerknij na kod niżej.

1
2
3
4
5
6
7
8
9
10
from redis import Redis

redis_connection = Redis(decode_responses=True)

redis_connection.zadd("sorted_set_key", {"key1": 1})
redis_connection.zadd("sorted_set_key", {"key2": 2})
redis_connection.zadd("sorted_set_key", {"key3": 3})
redis_connection.zadd("sorted_set_key", {"key4": 4})

print(redis_connection.zrange("sorted_set_key", 0, -1))

Którego efektem jest

['key1', 'key2', 'key3', 'key4']

Jeśli zmodyfikujemy wagi elementów np. na:

1
2
3
4
5
6
7
8
9
10
from redis import Redis

redis_connection = Redis(decode_responses=True)

redis_connection.zadd("sorted_set_key", {"key1": 5})
redis_connection.zadd("sorted_set_key", {"key2": 2})
redis_connection.zadd("sorted_set_key", {"key3": 6})
redis_connection.zadd("sorted_set_key", {"key4": 10})

print(redis_connection.zrange("sorted_set_key", 0, -1))

To otrzymamy kolejność zgodną z rosnącym porządkowaniem elementów, czyli:

['key2', 'key1', 'key3', 'key4']

Skoro mamy do czynienia z kolekcją, która zachowuje kolejność, to nasuwa się oczywiście pytanie: Co jeśli zdefiniujemy tę samą wartość score kilka razy? Jaka wtedy będzie kolejność?

Spieszę z odpowiedzią: klucze wtedy będą posortowane alfabetycznie. Taką sytuację pokazuje kod:

1
2
3
4
5
6
7
8
9
10
from redis import Redis

redis_connection = Redis(decode_responses=True)

redis_connection.zadd("sorted_set_key", {"key2": 1})
redis_connection.zadd("sorted_set_key", {"key1": 1})
redis_connection.zadd("sorted_set_key", {"key4": 1})
redis_connection.zadd("sorted_set_key", {"key3": 1})

print(redis_connection.zrange("sorted_set_key", 0, -1))

którego efektem będzie:

['key1', 'key2', 'key3', 'key4']

Komenda ZRANGE pozwala dostać się do wag. Wystarczy, że kod wyżej, nieco zmodyfikujemy, dodając do zrange nazwany parametr withscores o wartości True. Efektem programu po zmianie będzie:

[('key1', 1.0), ('key2', 1.0), ('key3', 1.0), ('key4', 1.0)]

Z ciekawych rzeczy, to istnieją komendy ZPOPMAX oraz ZPOPMIN (oraz ich blokujące odpowiedniki o których naturze wspomniałem w poprzednim artykule). Pozwalają one pobrać element o największej lub najmniejsze wadze.

Poleceniem ZINCRBY możemy zwiększyć wagę konkretnego elementu, ZCOUNT służy do wybrania elementów z pomiędzy zadanego przedziału wag a ZSCORE po prostu zwróci wagę podanego elementu.

Hashe

Czyli po prostu mapy. Słowniki. Tablice asocjacyjne. Nazw jest dużo, ale idea ta sama – struktura danych przechowująca klucze i odpowiadające im wartości.

1
2
3
4
5
6
7
from redis import Redis

redis_connection = Redis(decode_responses=True)

hash_key = 'test_hash'

redis_connection.hset(hash_key, 'key', 'value')

Powyższy kawałek kodu pozwala na stworzenie pod kluczem „test_hash” właśnie słownika, który będzie miał jeden klucz „key” o wartości „value”. Do dodania elementu do hasha używaliśmy polecenia HSET. Jeśli dodawany klucz będzie istniał to zostanie nadpiany. Aby pobrać wszystkie elementy, możemy użyć HGETALL:

1
2
3
127.0.0.1:6379> hgetall test_hash
1) "key"
2) "value"

Do pracy z tym typem danych mamy jeszcze kilka poleceń:

  • HKEYS – zwraca wszystkie klucze słownika
  • HVALS – analogicznie jak wyżej, zwraca wszystkie wartości
  • HEXISTS – sprawdza czy dany klucz istnieje w słowniku

Zmierzając do brzegu

Część czwarta to kolejne kilka metrów wgłąb Redisa, a dokładniej typów danych, jakie on oferuje. Poznaliśmy zbiory oraz ich wariację – zbiory posortowane. Niewątpliwą ich zaletą jest to ze złożoność obliczeniowa większości operacji jest bliska liniowej, co pozwala tworzyć wydajne aplikacje.

Dzięki za wizytę,
Mateusz Mazurek
Mateusz M.

Ostatnie wpisy

Podsumowanie: luty i marzec 2024

Ostatnio tygodnie były tak bardzo wypełnione, że nie udało mi się napisać nawet krótkiego podsumowanie. Więc dziś zbiorczo podsumuję luty… Read More

1 tydzień ago

Podsumowanie: styczeń 2024

Zapraszam na krótkie podsumowanie miesiąca. Książki W styczniu przeczytałem "Homo Deus: Historia jutra". Książka łudząco podoba do wcześniejszej książki tego… Read More

2 miesiące ago

Podsumowanie roku 2023

Cześć! Zapraszam na podsumowanie roku 2023. Książki Zacznijmy od książek. W tym roku cel 35 książek nie został osiągnięty. Niemniej… Read More

3 miesiące ago

Podsumowanie: grudzień 2023

Zapraszam na krótkie podsumowanie miesiąca. Książki W grudniu skończyłem czytać Mein Kampf. Nudna książka. Ciekawsze fragmenty można by było streścić… Read More

4 miesiące ago

Praca zdalna – co z nią dalej?

Cześć, ostatnio w Internecie pojawiło się dużo artykułów, które nie były przychylne pracy zdalnej. Z drugiej strony większość komentarzy pod… Read More

4 miesiące ago

Podsumowanie: listopad 2023

Zapraszam na krótkie podsumowanie miesiąca. Książki W listopadzie dokończyłem cykl "Z mgły zrodzony" Sandersona. Tylko "Stop prawa" mi nie do… Read More

5 miesięcy ago