Czym jest Redis i do czego się go używa?

Podstawy Programowania
Oleksandr Vykhor
53
01-10-2026 12:49:45


Czym jest Redis i do czego się go używa?

Redis prędzej czy później pojawia się niemal w każdym projekcie webowym: jako cache, kolejka zadań w tle, magazyn sesji lub licznik w czasie rzeczywistym. W świecie Rails przez długi czas był nieodłącznym towarzyszem Sidekiqa i Action Cable. W tym artykule wyjaśnimy, czym jest Redis, jak działa, jakie struktury danych oferuje, omówimy najczęstsze zastosowania z przykładami w Ruby i Rails, jego ograniczenia oraz to, co zmieniło się w licencjonowaniu Redisa i w Rails 8.

Czym jest Redis?

Redis (REmote DIctionary Server) to magazyn danych w pamięci operacyjnej (in-memory). Trzyma dane w RAM i odwołuje się do nich po kluczu, dzięki czemu odczyt i zapis są bardzo szybkie: typowa operacja trwa wyraźnie mniej niż milisekundę.

Redisa stworzył Salvatore Sanfilippo w 2009 roku. Często nazywa się go bazą klucz–wartość, ale to niedopowiedzenie: Redis to serwer struktur danych. Wartością może być nie tylko tekst, lecz także lista, hash, zbiór, zbiór posortowany, strumień i wiele więcej, a dla każdego typu Redis udostępnia atomowe polecenia.

Dlaczego Redis jest tak szybki?

  • Dane żyją w pamięci. Odczyt nie wymaga dostępu do dysku.
  • Proste i wydajne struktury danych zaimplementowane w C.
  • Jednowątkowe wykonywanie poleceń. Polecenia wykonywane są po kolei, więc wewnątrz Redisa nie ma blokad ani wyścigów: każde polecenie jest atomowe. Od Redisa 6 sieciowe wejście-wyjście może korzystać z dodatkowych wątków, ale same polecenia nadal wykonują się sekwencyjnie.
  • Lekki protokół (RESP) i obsługa potokowania (pipelining), która pozwala klientowi wysłać wiele poleceń w jednej wymianie sieciowej.

Główne typy danych

Typ Co to jest Typowe zastosowanie String Tekst, liczba lub dane binarne do 512 MB Cache, liczniki, flagi List Uporządkowana lista tekstów Proste kolejki, ostatnie elementy Hash Pary pole–wartość w jednym kluczu Obiekty, profile użytkowników, ustawienia Set Unikalne, nieuporządkowane wartości Tagi, unikalni odwiedzający, „kto polubił” Sorted set Unikalne wartości z wagą, posortowane według wagi Rankingi, tablice wyników, planowanie Stream Dziennik zdarzeń, do którego można tylko dopisywać Event sourcing, przetwarzanie wiadomości Bitmap, HyperLogLog, Geo Struktury specjalistyczne Flagi aktywności, przybliżone liczenie unikalnych, wyszukiwanie w pobliżu

W Redisie 8 do głównej dystrybucji trafiły także dokumenty JSON, wyszukiwanie i zbiory wektorowe, które wcześniej wymagały osobnych modułów.

Krótko o redis-cli

$ redis-cli
127.0.0.1:6379> SET greeting "Hello"
OK
127.0.0.1:6379> GET greeting
"Hello"
127.0.0.1:6379> SET session:abc123 "user_42" EX 3600   # wygasa po 1 godzinie
OK
127.0.0.1:6379> TTL session:abc123
(integer) 3600
127.0.0.1:6379> INCR page:views
(integer) 1
127.0.0.1:6379> HSET user:42 name "Anna" city "Warsaw"
(integer) 2
127.0.0.1:6379> HGETALL user:42
1) "name"
2) "Anna"
3) "city"
4) "Warsaw"

Jedną z najważniejszych funkcji jest tu TTL (time to live, czas życia): każdemu kluczowi można nadać czas wygaśnięcia, po którym Redis automatycznie go usunie. Właśnie tego potrzeba do cache'u, sesji i tymczasowych tokenów.

Do czego używa się Redisa? Główne zastosowania

W Ruby z Redisem pracuje się przez gem redis:

# Gemfile: gem "redis"
require "redis"

REDIS = Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"))

1. Cache

Najpopularniejsze zastosowanie. Ciężkie zapytania SQL, wyniki wywołań zewnętrznych API czy wyrenderowane fragmenty HTML zapisuje się w Redisie, a przy kolejnym żądaniu zwraca natychmiast. W Rails wystarczy skonfigurować magazyn cache:

# config/environments/production.rb
config.cache_store = :redis_cache_store, {
  url: ENV.fetch("REDIS_URL", "redis://localhost:6379/1"),
  expires_in: 1.day
}
# W dowolnym miejscu aplikacji
def dashboard_stats
  Rails.cache.fetch("dashboard/stats/#{Date.current}", expires_in: 10.minutes) do
    {
      users: User.count,
      orders: Order.where(created_at: Date.current.all_day).count,
      revenue: Order.paid.sum(:total)
    }
  end
end

Pierwsze wywołanie wykonuje zapytania i zapisuje wynik, a kolejne wywołania przez 10 minut odczytują go bezpośrednio z Redisa.

2. Kolejki zadań w tle

Sidekiq, najpopularniejsze narzędzie do zadań w tle w Ruby, przechowuje swoje kolejki w Redisie. Wysyłka e-maili, generowanie raportów, przetwarzanie obrazów: wszystko to przenosi się poza żądanie webowe i obsługuje w procesach workerów.

# app/sidekiq/welcome_email_job.rb
class WelcomeEmailJob
  include Sidekiq::Job
  sidekiq_options queue: :mailers, retry: 5

  def perform(user_id)
    user = User.find(user_id)
    UserMailer.with(user: user).welcome.deliver_now
  end
end

# W kontrolerze lub modelu
WelcomeEmailJob.perform_async(user.id)

3. Sesje i dane tymczasowe

Przechowywanie sesji w Redisie pozwala kilku serwerom aplikacji je współdzielić, a TTL automatycznie usuwa stare sesje. To samo podejście sprawdza się przy kodach jednorazowych, tokenach resetu hasła i linkach potwierdzających e-mail.

4. Ograniczanie liczby żądań (rate limiting)

Atomowe liczniki z TTL pozwalają łatwo ograniczyć liczbę żądań, np. do 100 wywołań API na minutę na użytkownika:

class RateLimiter
  def initialize(redis: REDIS, limit: 100, period: 60)
    @redis = redis
    @limit = limit
    @period = period
  end

  def allowed?(identifier)
    window = Time.now.to_i / @period
    key = "rate:#{identifier}:#{window}"

    count, _ = @redis.multi do |tx|
      tx.incr(key)
      tx.expire(key, @period)
    end

    count <= @limit
  end
end

limiter = RateLimiter.new
limiter.allowed?("user:42") # => true

Ponieważ INCR jest atomowe, działa to poprawnie nawet wtedy, gdy wiele serwerów aplikacji jednocześnie korzysta z tego samego klucza. Od Rails 7.2 kontrolery mają też wbudowaną metodę rate_limit, która pod spodem korzysta z magazynu cache.

5. Czas rzeczywisty i pub/sub

Redis obsługuje model publikuj–subskrybuj: jeden proces publikuje wiadomość na kanale, a wszyscy subskrybenci natychmiast ją otrzymują. Action Cable wykorzystuje to do przekazywania wiadomości WebSocket między wieloma serwerami:

# config/cable.yml
production:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL", "redis://localhost:6379/2") %>
  channel_prefix: myapp_production

6. Tablice wyników i rankingi

Zbiory posortowane przechowują elementy uporządkowane według wagi, więc top 10 to jedno polecenie:

REDIS.zincrby("leaderboard", 50, "anna")
REDIS.zincrby("leaderboard", 30, "oleg")
REDIS.zincrby("leaderboard", 70, "maria")

REDIS.zrevrange("leaderboard", 0, 9, with_scores: true)
# => [["maria", 70.0], ["anna", 50.0], ["oleg", 30.0]]

REDIS.zrevrank("leaderboard", "anna") # => 1 (pozycja liczona od 0)

7. Rozproszone blokady

Gdy zadanie nie może wykonać się dwa razy jednocześnie na kilku serwerach (np. import według harmonogramu), można użyć blokady opartej na SET z opcją NX:

def with_lock(name, ttl: 30)
  token = SecureRandom.uuid
  acquired = REDIS.set("lock:#{name}", token, nx: true, ex: ttl)
  return false unless acquired

  begin
    yield
  ensure
    # Usuwamy blokadę tylko wtedy, gdy nadal należy do nas
    REDIS.eval(<<~LUA, keys: ["lock:#{name}"], argv: [token])
      if redis.call("get", KEYS[1]) == ARGV[1] then
        return redis.call("del", KEYS[1])
      end
      return 0
    LUA
  end
end

with_lock("daily_import") { DailyImport.run }

Trwałość danych: czy Redis traci dane po restarcie?

Choć Redis trzyma dane w pamięci, potrafi zapisywać je na dysk:

  • RDB (migawki): Redis okresowo zapisuje pełną migawkę danych. Kompaktowe i szybkie w odtwarzaniu, ale zmiany od ostatniej migawki mogą przepaść.
  • AOF (append-only file): każde polecenie zapisu jest dopisywane do dziennika. Ryzyko utraty danych jest znacznie mniejsze (domyślnie maksymalnie około sekundy), ale plik jest większy.
  • Oba naraz: częsty wybór, gdy dane są ważne.
  • Bez trwałości: wystarcza dla czystego cache'u, który można odbudować.

Gdy kończy się pamięć: wypieranie danych

Ustawienie maxmemory ogranicza ilość RAM, z której może korzystać Redis, a maxmemory-policy określa, co dzieje się po osiągnięciu limitu:

  • allkeys-lru lub allkeys-lfu: usuwaj klucze najdawniej lub najrzadziej używane. Dobry wybór dla cache'u.
  • noeviction: odrzucaj nowe zapisy. Konieczne dla Sidekiqa i innych kolejek, bo po cichu usunięte zadania są znacznie gorsze niż błąd.

Dlatego na produkcji cache i kolejkę zadań często trzyma się w osobnych instancjach Redisa z różnymi ustawieniami.

Instalacja Redisa na Ubuntu

sudo apt update
sudo apt install redis-server

sudo systemctl enable --now redis-server
redis-cli ping   # => PONG

Kluczowe linie w /etc/redis/redis.conf:

bind 127.0.0.1 -::1          # nasłuchuj tylko na localhost
protected-mode yes
requirepass a-long-random-password   # lub użyj użytkowników ACL
maxmemory 512mb
maxmemory-policy allkeys-lru         # dla cache'u; noeviction dla Sidekiqa
appendonly yes                       # włącz AOF, jeśli dane są ważne

Po zmianie konfiguracji zrestartuj usługę: sudo systemctl restart redis-server.

Ograniczenia i pułapki

  • Pamięć RAM jest droga i ograniczona. Wszystkie dane muszą zmieścić się w pamięci, więc Redis nie nadaje się do przechowywania dużych ilości danych.
  • To nie zamiennik głównej bazy danych. Nie ma relacji, SQL ani złożonych zapytań, a gwarancje trwałości są słabsze niż w PostgreSQL czy MySQL. Redis uzupełnia bazę danych, a nie ją zastępuje.
  • Bezpieczeństwo. Nigdy nie wystawiaj portu 6379 do internetu. Otwarte instancje Redisa bez hasła są regularnie wyszukiwane i atakowane.
  • Wolne polecenia blokują wszystkich. Ponieważ polecenia wykonują się po kolei, KEYS * na dużej bazie lub ogromne SMEMBERS zatrzymają wszystkich pozostałych klientów. Zamiast KEYS używaj SCAN.
  • Unieważnianie cache'u. Cache może zwrócić nieaktualne dane. Starannie przemyśl TTL i moment czyszczenia kluczy.

Redis, licencje i Valkey

Przez wiele lat Redis był udostępniany na liberalnej licencji BSD. W 2024 roku Redis Inc. przeszła na licencje z dostępnym kodem źródłowym, które nie są uznawane za open source (RSALv2 i SSPL). W odpowiedzi Linux Foundation i duzi dostawcy chmury stworzyli Valkey, w pełni otwarty fork Redisa 7.2, zgodny z klientami i poleceniami Redisa.

W 2025 roku, wraz z premierą Redisa 8, jako jedna z opcji doszła otwarta licencja AGPLv3. Dziś aktywnie rozwijane są zarówno Redis, jak i Valkey. Dla typowej aplikacji Rails wybór rzadko ma znaczenie: gem redis, Sidekiq i cache Rails działają z oboma.

Czy w Rails 8 nadal potrzebujesz Redisa?

Rails 8 wprowadził trio „Solid”, które domyślnie korzysta z głównej bazy danych zamiast z Redisa:

  • Solid Cache: cache w tabeli bazy danych (na dysku, więc może być znacznie większy niż RAM).
  • Solid Queue: zadania w tle dla Active Job.
  • Solid Cable: adapter dla Action Cable.

Oznacza to, że nowa aplikacja Rails może działać w ogóle bez Redisa, co upraszcza wdrożenie na małym VPS. Redis nadal jest świetnym wyborem, gdy potrzebujesz:

  • Sidekiqa i jego ekosystemu (funkcje Pro/Enterprise, panel webowy, wiele wtyczek);
  • jak najmniejszych opóźnień dla cache'u i liczników;
  • zbiorów posortowanych, strumieni, pub/sub, atomowych liczników, rate limitingu i blokad;
  • bardzo dużego obciążenia z wieloma wiadomościami WebSocket w czasie rzeczywistym.

Dobre praktyki

  1. Zawsze ustawiaj TTL dla cache'u i danych tymczasowych.
  2. Używaj czytelnych nazw kluczy z prefiksami: user:42:profile, rate:api:42.
  3. Oddziel cache od kolejek: osobne instancje lub przynajmniej osobne bazy z odpowiednimi politykami wypierania.
  4. Skonfiguruj maxmemory, aby Redis nie zajął całej pamięci serwera.
  5. Chroń Redisa: nasłuchuj tylko na localhost lub w sieci prywatnej, ustaw hasło lub ACL i używaj TLS między serwerami.
  6. Unikaj KEYS i ogromnych wartości; używaj SCAN i trzymaj wartości małe.
  7. Monitoruj: INFO, SLOWLOG, zużycie pamięci i współczynnik trafień cache'u.
  8. Nie trzymaj jedynej kopii ważnych danych w Redisie. Źródłem prawdy powinna być główna baza danych.

Podsumowanie

Redis to bardzo szybki magazyn danych w pamięci z bogatymi strukturami danych. Używa się go do cache'owania, kolejek zadań w tle, sesji, ograniczania liczby żądań, komunikacji w czasie rzeczywistym, rankingów i rozproszonych blokad. Nie zastępuje relacyjnej bazy danych, ale przejmuje wszystko, co ma być szybkie, tymczasowe lub współdzielone między serwerami. W Rails 8 można zacząć bez Redisa dzięki Solid Cache, Solid Queue i Solid Cable, ale wraz ze wzrostem obciążenia i wymagań Redis (lub Valkey) pozostaje jednym z najbardziej przydatnych narzędzi programisty webowego.


Wstecz