redis

Redis w praktyce backend developera


Czym Redis nie jest

Redis nie jest klasyczną bazą danych, taką jak PostgreSQL czy MySQL, w których dane przechowywane są trwale na dysku.

Jest to baza danych działająca głównie w pamięci RAM (tzw. in-memory database), dzięki czemu operacje na danych są bardzo szybkie. Redis wykorzystuje model klucz-wartość, w którym dane są powiązane z unikalnymi kluczami, co umożliwia ich błyskawiczne wyszukiwanie.

Warto jednak pamiętać, że Redis oferuje również bardziej zaawansowane struktury danych, takie jak listy, zbiory czy hashe, dzięki czemu może być wykorzystywany w wielu scenariuszach backendowych.

W praktyce Redis jest często wykorzystywany jako element infrastruktury wspierający aplikację backendową. Poniżej przedstawiam kilka realnych zastosowań, z którymi można spotkać się w projektach.

Jak uruchomić Redis lokalnie?

Najprostszym sposobem uruchomienia Redis lokalnie jest użycie Dockera.

Utwórz plik docker-compose.yml z poniższą konfiguracją:

Następnie uruchom serwis poleceniem:

Po uruchomieniu Redis będzie dostępny lokalnie pod adresem:

Use case #1 – JWT Blocklist

Tokeny JWT są stateless, co oznacza, że serwer nie przechowuje ich w bazie danych. Dzięki temu uwierzytelnianie jest szybkie i skalowalne, ale pojawia się problem – w jaki sposób unieważnić token przed jego naturalnym wygaśnięciem, na przykład po wylogowaniu użytkownika.

Jednym z rozwiązań jest wykorzystanie Redis jako tzw. blocklisty. Podczas wylogowania identyfikator tokenu (jti) zostaje zapisany w Redisie. Przy każdym żądaniu aplikacja sprawdza, czy dany identyfikator znajduje się na liście zablokowanych tokenów.

Klucz w Redisie otrzymuje czas życia (TTL) równy pozostałemu czasowi ważności tokenu. Dzięki temu wpis zostanie automatycznie usunięty po wygaśnięciu tokenu, co zapobiega niepotrzebnemu zajmowaniu pamięci.

Baza in-memory sprawdza się w tym scenariuszu bardzo dobrze ze względu na bardzo szybkie operacje odczytu oraz możliwość ustawiania czasu życia kluczy.

Use case #2 – Rate limiting

Bardzo często podczas tworzenia REST API pojawia się potrzeba ograniczenia liczby żądań, jakie użytkownik może wykonać w określonym przedziale czasowym. Mechanizm ten nazywany jest rate limiting.

Redis sprawdza się w tym scenariuszu szczególnie dobrze, ponieważ udostępnia proste operacje na licznikach. Za pomocą polecenia INCR możemy zwiększać licznik żądań dla danego użytkownika, natomiast EXPIRE pozwala ustawić czas życia klucza, który określa długość okna czasowego.

Dzięki temu Redis może przechowywać informację o liczbie wykonanych żądań, a po upływie zadanego czasu licznik zostanie automatycznie zresetowany. Poniższy przykład pokazuje prostą implementację rate limiting z wykorzystaniem tych mechanizmów.

Use case #3 – Kolejki i zadania w tle (RQ)

W wielu aplikacjach backendowych potrzebujemy przetwarzać zadania w tle, np. wysyłanie maili, generowanie raportów czy przetwarzanie dużych danych.

Zamiast blokować użytkownika w REST API, możemy użyć kolejek zadań. Redis świetnie się do tego nadaje, ponieważ działa w pamięci RAM i oferuje bardzo szybkie operacje zapisu i odczytu danych. Dzięki temu dodawanie i pobieranie zadań z kolejki jest praktycznie natychmiastowe.

W tym przykładzie użyjemy RQ (Redis Queue) – prostej biblioteki do kolejek w Pythonie.

Zadanie do wykonania

Tworzymy funkcję, która reprezentuje zadanie:

Dodawanie zadania do kolejki (enqueue)

Skrypt enqueue_job.py dodaje zadanie do kolejki Redis. To producer – tworzy zadania, ale ich nie wykonuje:

Worker – proces wykonujący zadania

Worker to osobny proces, który nasłuchuje kolejki w Redisie i wykonuje zadania w tle (asynchronicznie):

Ważne: na Windows worker najlepiej uruchamiać w kontenerze Linux (Docker), bo Python na Windows nie wspiera mechanizmu fork, którego używa RQ.

W tym przykładzie wszystkie komponenty działają w sieci Dockera, dlatego używamy adresu redis:6379, czyli nazwy serwisu z konfiguracji Docker Compose.

Diagram przepływu

  • enqueue_job.py – dodaje zadanie do kolejki
  • Redis – przechowuje kolejkę w pamięci
  • Worker – pobiera zadania i wykonuje je w tle

Use case #4 – Cache aplikacyjny

Ten system bardzo często wykorzystywany jest jako cache aplikacyjny, który przechowuje często odczytywane dane w pamięci RAM. Dzięki temu możemy ograniczyć liczbę zapytań do głównej bazy danych i znacząco przyspieszyć działanie aplikacji.

W tym przykładzie zastosujemy popularny wzorzec cache-aside.
Aplikacja najpierw próbuje odczytać dane z Redis, a jeśli ich tam nie ma, pobiera je z bazy danych i zapisuje w cache na określony czas.

W powyższym przykładzie:

  • aplikacja najpierw sprawdza, czy dane użytkownika znajdują się w Redis
  • jeśli tak, zwraca je bez wykonywania zapytania do bazy
  • jeśli nie, pobiera dane z bazy i zapisuje je w cache na 60 sekund przy użyciu setex

Dzięki temu kolejne zapytania o tego samego użytkownika mogą zostać obsłużone bezpośrednio z pamięci Redis.

Ważne: W praktycznych systemach należy również pamiętać o cache invalidation, czyli usuwaniu lub odświeżaniu wpisów w Redis, gdy dane w bazie zostaną zmienione.

Podsumowanie

Redis jest niezwykle wszechstronnym narzędziem, które może pełnić w systemach backendowych wiele różnych ról. W artykule pokazaliśmy kilka praktycznych zastosowań: przechowywanie blocklisty tokenów JWT, implementację rate limitingu, obsługę kolejek zadań w tle oraz cache aplikacyjny.

Dzięki pracy w pamięci RAM Redis oferuje bardzo szybkie operacje odczytu i zapisu, co sprawia, że świetnie sprawdza się jako element infrastruktury wspierający aplikacje backendowe.

W praktycznych systemach Redis rzadko zastępuje główną bazę danych – znacznie częściej działa jako uzupełnienie architektury, przyspieszając działanie aplikacji i odciążając inne komponenty systemu.