Redis 8, struktury danych i pytanie o licencję
Redis trzyma dane w pamięci operacyjnej i udostępnia je przez sieć, dzięki czemu odczyt trwa ułamek milisekundy zamiast kilku. Bieżące wydanie z końca lipca 2026 nosi numer 8.10.
Największym nieporozumieniem wokół tego narzędzia jest sprowadzanie go do bufora podręcznego. Buforowanie jest najczęstszym zastosowaniem, natomiast realna wartość leży w strukturach danych, które robią rzeczy, których w zwykłej bazie robi się kilkoma zapytaniami i blokadą.
Licencja, czyli rzecz do rozstrzygnięcia najpierw
Sprawa jest zagmatwana i warto ją rozwikłać, bo pojawia się przy każdej rozmowie o tym narzędziu.
Przez lata projekt był na licencji BSD. W marcu 2024 roku właściciel zmienił ją na wariant, którego organizacja opiniująca licencje otwarte nie uznaje za otwarty. Powstał wtedy fork o nazwie Valkey, wzięty z ostatniego wydania na dawnej licencji, prowadzony pod skrzydłami fundacji i wspierany przez kilku dużych dostawców chmurowych.
Od wersji 8.0 z maja 2025 roku projekt oferuje wybór między trzema licencjami, w tym AGPLv3, która jest uznawana za otwartą. Formalnie więc powrót nastąpił, choć na innej licencji niż pierwotna.
Co to znaczy w praktyce. Dla zdecydowanej większości użytkowników nic, bo AGPL nakłada obowiązki na tego, kto udostępnia zmodyfikowaną wersję jako usługę, a nie na tego, kto łączy się z bazą przez sieć w swojej aplikacji. Znaczenie pojawia się przy dwóch sytuacjach: gdy modyfikujesz kod serwera i udostępniasz go innym, oraz gdy dział prawny w Twojej firmie ma zapisaną regułę odrzucającą tę rodzinę licencji bez wnikania w szczegóły.
Oba projekty żyją i wydają nowe wersje w podobnym tempie, więc wybór między nimi jest realny, a nie akademicki. Valkey pozostaje na dawnej licencji BSD i jest domyślną opcją u części dostawców chmurowych, co bywa argumentem rozstrzygającym, gdy i tak korzystasz z ich usługi zarządzanej.
Struktury, które robią różnicę
Warto wyjść poza zapisywanie i odczytywanie łańcuchów znaków, bo to jest najmniej ciekawa część.
Zbiór uporządkowany trzyma elementy z przypisaną wartością i pozwala pytać o zakres. To jest gotowy ranking: dopisanie wyniku gracza i pobranie dziesięciu najlepszych to dwie operacje, bez sortowania i bez blokady.
Lista działa jak kolejka z obu stron. Dopisanie zadania na jeden koniec i pobranie z drugiego, z opcją oczekiwania na pojawienie się elementu, daje kolejkę zadań bez stawiania osobnego systemu.
Zbiór liczności przybliżonej odpowiada na pytanie o liczbę unikalnych elementów, zajmując stałą, niewielką ilość pamięci niezależnie od skali. Liczenie unikalnych odwiedzających w serwisie z milionami wejść mieści się w kilkunastu kilobajtach kosztem kilku procent dokładności.
Strumień jest dziennikiem zdarzeń z grupami odbiorców i potwierdzaniem odbioru. To jest sensowna droga przy przetwarzaniu zdarzeń, gdy pełny system kolejkowy byłby przerostem.
Nowsza rzecz, dodana w ósmej wersji, to zbiór wektorów. Pozwala trzymać osadzenia i wyszukiwać po podobieństwie bez stawiania osobnej bazy wektorowej, co przy niewielkich zbiorach bywa wystarczające i znacznie prostsze operacyjnie.
Buforowanie i to, gdzie się psuje
Skoro buforowanie jest najczęstszym zastosowaniem, warto opisać, gdzie zwykle zawodzi.
def pobierz_produkt(id: str) -> dict:
klucz = f"produkt:{id}"
dane = r.get(klucz)
if dane:
return json.loads(dane)
produkt = baza.pobierz_produkt(id)
r.set(klucz, json.dumps(produkt), ex=300)
return produktTen kod jest poprawny i ma trzy problemy, które ujawniają się dopiero pod obciążeniem.
Pierwszy to jednoczesne wygaśnięcie wielu kluczy. Jeśli tysiąc produktów trafiło do bufora w tej samej sekundzie po wdrożeniu, wszystkie wygasną w tej samej sekundzie pięć minut później, a baza dostanie tysiąc zapytań naraz. Rozwiązaniem jest dodanie losowej zmienności do czasu życia.
Drugi to wyścig przy jednym gorącym kluczu. Wygaśnięcie klucza pobieranego przez pięćset równoległych żądań daje pięćset zapytań do bazy, bo każde żądanie widzi pusty bufor. Rozwiązaniem jest blokada pozwalająca odświeżyć wartość jednemu żądaniu, podczas gdy pozostałe czekają albo dostają wartość poprzednią.
Trzeci to unieważnianie. Zmiana produktu w bazie nie zmienia niczego w buforze, więc użytkownicy widzą starą cenę przez pięć minut. Usuwanie klucza przy zapisie jest właściwym rozwiązaniem i wymaga, żeby każde miejsce zmieniające dane o tym pamiętało, co przy większej aplikacji jest trudniejsze, niż wygląda.
Warto też pamiętać o zasadzie ogólnej: bufor ma przyspieszać, a nie być źródłem prawdy. Aplikacja, która przestaje działać po restarcie tego serwera, nie używa bufora, tylko trzyma w nim dane.
Trwałość i co się dzieje przy awarii
Dane leżą w pamięci, więc pytanie o to, co zostaje po zaniku zasilania, jest podstawowe.
Są dwa mechanizmy. Pierwszy zapisuje migawkę stanu co jakiś czas, drugi dopisuje każdą operację do dziennika. Migawka jest tania i traci wszystko od ostatniego zapisu. Dziennik traci mniej i kosztuje więcej na zapisie oraz przy starcie.
Kluczowa rzecz do zrozumienia: nawet wariant dopisujący każdą operację domyślnie synchronizuje z dyskiem raz na sekundę, więc przy awarii tracisz do sekundy zapisów. Ustawienie synchronizacji przy każdej operacji usuwa to okno i mocno obniża przepustowość.
Wniosek praktyczny jest prosty i często pomijany. To nie jest baza do danych, których utrata jest niedopuszczalna. Sesje, bufory, liczniki, kolejki zadań z możliwością ponowienia, owszem. Salda kont i zamówienia, nie.
Replikacja rozwiązuje inny problem niż trwałość: dostępność i rozłożenie odczytów. Warto o tym pamiętać, bo replikacja jest asynchroniczna, więc potwierdzenie zapisu nie znaczy, że replika już go ma.
Zbiory wektorów i kiedy wystarczą
Nowy typ danych z ósmej wersji zasługuje na osobne omówienie, bo rozwiązuje problem, do którego zwykle stawia się osobną bazę.
Rzecz polega na przechowywaniu osadzeń, czyli wektorów liczb reprezentujących znaczenie tekstu, i wyszukiwaniu tych najbardziej podobnych do zapytania. To jest podstawa aplikacji odpowiadających na pytania z dokumentów.
Zaleta jest operacyjna, nie jakościowa. Jeśli już masz ten serwer w projekcie do buforowania i sesji, dołożenie wyszukiwania po podobieństwie nie wymaga kolejnej usługi do utrzymania, kolejnego zestawu kopii zapasowych i kolejnego miejsca, w którym coś może paść.
Granica jest równie wyraźna. Dane leżą w pamięci, więc zbiór osadzeń musi się w niej zmieścić razem z resztą, a osadzenia są duże: sto tysięcy fragmentów tekstu w typowym wymiarze to kilkaset megabajtów. Przy milionach dokumentów właściwym wyborem jest baza przeznaczona do tego, która trzyma indeks na dysku.
Praktyczna reguła brzmi: do kilkudziesięciu tysięcy fragmentów to jest dobre rozwiązanie i oszczędza całą warstwę infrastruktury. Powyżej warto policzyć pamięć, zanim okaże się, że wyszukiwanie wypycha z pamięci bufor, który wcześniej działał.
Warto też pamiętać o trwałości. Zbiór osadzeń zbudowany kosztem wielu wywołań modelu, przechowywany wyłącznie tutaj, przy awarii trzeba odtworzyć, a to kosztuje ponownie. Trzymanie źródłowych fragmentów w bazie relacyjnej i traktowanie wyszukiwania jako warstwy odtwarzalnej jest tańsze niż zapewnianie trwałości w tym miejscu.
Wpięcie w aplikację
Kilka rzeczy dotyczących połączenia oszczędza kłopotów przy pierwszym wdrożeniu.
Pula połączeń jest obowiązkowa. Otwieranie połączenia przy każdym żądaniu marnuje czas na uzgadnianie, a przy większym ruchu wyczerpuje limit deskryptorów. Biblioteki klienckie mają pulę wbudowaną i wystarczy jej użyć zamiast tworzyć klienta w funkcji obsługującej żądanie.
Limity czasu ustaw jawnie. Serwer, który przestał odpowiadać, bez limitu zawiesza żądanie, a przy buforze jest to szczególnie niedobre, bo warstwa mająca przyspieszać zatrzymuje aplikację. Sensowna droga to krótki limit i przejście do źródła danych, gdy bufor milczy.
Zachowanie przy niedostępności warto rozstrzygnąć świadomie. Aplikacja, która przy braku bufora sięga do bazy, przetrwa awarię wolniej i działając. Aplikacja, która zwraca błąd, przewróci się cała z powodu warstwy, która miała być opcjonalna.
Przy pamięci agenta i danych sesyjnych warto rozważyć, gdzie postawić granicę między tym serwerem a warstwą trwałą, na przykład opisaną w pamięci w LangGraph. Stan bieżącej rozmowy pasuje tutaj, a to, co ma przetrwać tygodnie, do bazy.
Redis kontra alternatywy
| Rozwiązanie | Model | Trwałość | Kiedy wybrać |
|---|---|---|---|
| Redis | Struktury w pamięci | Konfigurowalna, z oknem utraty | Bufor, sesje, kolejki, liczniki |
| Valkey | To samo, licencja BSD | Taka sama | Licencja jest czynnikiem, albo używasz chmury z nim |
| Upstash | Zgodny interfejs, rozliczenie za żądania | Po stronie dostawcy | Ruch nierówny, środowiska bezserwerowe |
| Baza relacyjna | Dane na dysku | Pełna | Dane, których utrata jest niedopuszczalna |
Dwa pierwsze wiersze są zgodne na poziomie protokołu, więc przejście między nimi zwykle sprowadza się do zmiany adresu. Różnica leży w licencji i w tym, co oferuje Twój dostawca chmurowy, a nie w możliwościach.
Trzeci wiersz warto rozważyć przy ruchu nierównym. Klasyczne wdrożenie płaci za utrzymywaną pamięć niezależnie od użycia, a rozliczenie za żądania pasuje lepiej do aplikacji, która przez większość doby nic nie robi. Przy stałym, wysokim ruchu opłacalność się odwraca.
Ostatni wiersz to nie alternatywa, tylko przypomnienie. Sięganie po to narzędzie do danych wymagających gwarancji trwałości jest częstym błędem architektonicznym, wynikającym z tego, że jest szybkie i wygodne.
Zastosowania poza buforowaniem
Trzy przypadki, w których to narzędzie wygrywa wyraźnie z alternatywami.
Limitowanie tempa żądań. Licznik z czasem życia albo okno przesuwne oparte na zbiorze uporządkowanym dają mechanizm działający poprawnie przy wielu instancjach aplikacji, czego licznik w pamięci procesu nie daje.
Sesje użytkowników. Wspólne miejsce widoczne dla wszystkich instancji, z automatycznym wygasaniem, bez potrzeby przypinania użytkownika do konkretnego serwera.
Blokady rozproszone. Ustawienie klucza wyłącznie wtedy, gdy nie istnieje, razem z czasem życia, daje mechanizm zapewniający, że zadanie cykliczne wykona się raz, a nie na każdej instancji.
Przy ostatnim warto znać granicę. Poprawna blokada wymaga losowego identyfikatora właściciela i zwolnienia wyłącznie przez tego, kto ją założył, inaczej wolne zadanie zwolni blokadę przejętą już przez inne. To jest miejsce, w którym warto użyć gotowej biblioteki zamiast pisać samodzielnie.
Praktyka codziennej pracy
Kilka rzeczy oszczędza kłopotów przy dłuższym utrzymaniu.
Ustaw limit pamięci i zasadę usuwania kluczy. Bez tego serwer rośnie, aż zabraknie pamięci na maszynie, a wtedy zachowanie zależy od systemu i bywa nieprzyjemne. Zasada usuwająca najrzadziej używane klucze jest właściwa dla bufora, a odrzucanie zapisów dla danych, które nie mogą zniknąć.
Nadawaj czas życia wszystkiemu, co jest buforem. Klucz bez czasu życia zostaje na zawsze, a przy zmianie schematu kluczy stare zostają jako śmieci, których nikt nie posprząta.
Nie używaj polecenia przeglądającego wszystkie klucze na produkcji. Blokuje serwer na czas przejścia przez cały zbiór, a przy milionach kluczy jest to zauważalne dla wszystkich. Do przeglądania służy wariant iterujący porcjami.
Grupuj operacje przy wielu wywołaniach. Sto osobnych zapytań to sto przelotów sieciowych, a to samo wysłane w jednej paczce zajmuje jeden. Przy odczytach w pętli różnica jest kilkudziesięciokrotna.
Ostatnia rzecz to skrypty wykonywane po stronie serwera. Pozwalają wykonać kilka operacji niepodzielnie, co przy licznikach i blokadach usuwa wyścigi, których nie da się usunąć po stronie klienta.
Typowe błędy
Pierwszy to brak losowej zmienności w czasie życia kluczy. Masowe wygaśnięcie w jednej sekundzie przenosi całe obciążenie na bazę.
Drugi to traktowanie tego jako źródła prawdy. Aplikacja przestająca działać po restarcie tego serwera nie buforuje, tylko przechowuje dane w miejscu bez gwarancji.
Trzeci to brak limitu pamięci i zasady usuwania. Serwer rośnie do wyczerpania pamięci maszyny, a objawy pojawiają się najpierw w innych usługach.
Czwarty to przeglądanie wszystkich kluczy poleceniem blokującym. Przy dużym zbiorze zatrzymuje to serwer dla wszystkich klientów.
Piąty to blokada rozproszona bez identyfikatora właściciela. Wolne zadanie zwalnia wtedy blokadę należącą już do innego procesu.
Szósty to zakładanie, że replika ma dane natychmiast po potwierdzeniu zapisu. Replikacja jest asynchroniczna, więc odczyt z repliki może zwrócić stan sprzed chwili.
FAQ
Czy Redis jest open source?
Od wersji 8.0 z maja 2025 roku dostępny jest wybór licencji, w tym AGPLv3, która jest uznawana za otwartą. Wcześniej, od marca 2024, obowiązywał wariant nieuznawany za otwarty i to właśnie doprowadziło do powstania forku Valkey.
Czym różni się Valkey?
Licencją i zarządzaniem. Valkey wyszedł z ostatniego wydania na dawnej licencji BSD, jest prowadzony pod skrzydłami fundacji i wspierany przez kilku dużych dostawców chmurowych. Oba projekty pozostają zgodne na poziomie protokołu, więc przejście zwykle sprowadza się do zmiany adresu.
Czy dane przetrwają restart?
Zależy od konfiguracji trwałości i zawsze z pewnym oknem utraty. Domyślny wariant dopisujący operacje synchronizuje z dyskiem raz na sekundę, więc przy awarii tracisz do sekundy zapisów. Do danych, których utrata jest niedopuszczalna, właściwa jest baza relacyjna.
Czy to nadaje się na kolejkę zadań?
Do prostych przypadków tak, bo lista z blokującym pobraniem daje kolejkę bez dodatkowej infrastruktury. Przy wymogu gwarantowanego dostarczenia, ponawiania i widoczności zadań warto rozważyć system przeznaczony do tego, bo tutaj część tych rzeczy trzeba zbudować samodzielnie.
Kiedy wybrać rozwiązanie zarządzane?
Przy ruchu nierównym i w środowiskach bezserwerowych, gdzie rozliczenie za żądania pasuje lepiej niż opłata za utrzymywaną pamięć. Upstash jest tu typowym wyborem, a przy stałym wysokim ruchu klasyczne wdrożenie wychodzi taniej.
Dokumentacja stoi na stronie projektu, a wydania w repozytorium na GitHubie.