Mateusz Mazurek – programista z pasją

Blog o Pythonie i kilku innych technologiach. Od developera dla wszystkich.

Programowanie Programowanie webowe

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

A może wolisz nowości na mail?

Subskrybuj
Powiadom o
guest

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

3 komentarzy
Inline Feedbacks
View all comments