Przechowywanie sesji i wzorce tabel wyników
Używać ElastiCache do odciążania serwerów aplikacji z przechowywania stanu sesji HTTP oraz implementować tablice wyników w czasie rzeczywistym za pomocą uporządkowanych zbiorów Redis.
Przechowywanie sesji i wzorce tabel wyników to bezpłatna lekcja AWS Solutions Architect na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AWS Solutions Architect, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AWS Solutions Architect zawiera 4 lekcji w sumie.
Problem z sesjami po stronie serwera
Tradycyjne aplikacje internetowe przechowują dane sesji w pamięci serwera. Działa to w przypadku pojedynczego serwera, ale przestaje działać po skalowaniu poziomym — jeśli kolejne żądanie użytkownika zostanie przekierowane do innej instancji EC2, ta instancja nie zna sesji użytkownika, a użytkownik zostaje wylogowany. Sesje lepkie (powinowactwo sesji w module równoważenia obciążenia) rozwiązują ten problem tylko częściowo, ale zmniejszają skuteczność równoważenia obciążenia. Skalowalne rozwiązanie polega na przeniesieniu stanu sesji do współdzielonego magazynu o małym opóźnieniu, dostępnego dla wszystkich instancji — dokładnie taką funkcję zapewnia ElastiCache Redis.
Redis do przechowywania sesji
Przechowywanie sesji w Redis zapewnia: odczyty sesji w czasie krótszym niż milisekunda na wszystkich serwerach aplikacji, wbudowany TTL do automatycznego wygaszania sesji, atomowe aktualizacje sesji zapobiegające wyścigom oraz możliwość natychmiastowego unieważnienia sesji przez usunięcie klucza. Aplikacja przechowuje identyfikator sesji w pliku cookie, a przy każdym żądaniu wyszukuje ten identyfikator w Redis, aby pobrać dane sesji. Wszystkie serwery aplikacji korzystają z tego samego Redis, więc dowolny serwer może obsłużyć żądanie dowolnego użytkownika.
# Session storage with Redis (Python Flask example)
import redis, json, uuid
from datetime import timedelta
redis_client = redis.Redis(host='prod-redis-primary', port=6379)
SESSION_TTL = int(timedelta(hours=8).total_seconds())
def create_session(user_id):
session_id = str(uuid.uuid4())
session_data = {'user_id': user_id, 'logged_in': True}
redis_client.setex(f'session:{session_id}', SESSION_TTL, json.dumps(session_data))
return session_id
def get_session(session_id):
data = redis_client.get(f'session:{session_id}')
return json.loads(data) if data else NoneTTL sesji i przesuwane wygasanie
Stały TTL oznacza, że sesja wygasa N sekund po utworzeniu, niezależnie od aktywności. Przesuwany TTL (przedłużanie czasu wygaśnięcia przy każdym dostępie) jest wygodniejszy dla użytkownika — sesja wygasa N sekund po ostatnim dostępie. W Redis można zaimplementować przesuwany TTL, wywołując EXPIRE (lub EXPIREAT) dla klucza sesji przy każdym pomyślnym odczycie sesji, aby zresetować odliczanie do wygaśnięcia. Dzięki temu aktywni użytkownicy nie zostaną nieoczekiwanie wylogowani, a nieaktywne sesje wygasną automatycznie, zwalniając pamięć.
# Sliding TTL session implementation
def get_session_with_sliding_ttl(session_id, redis_client, ttl_seconds=1800):
session_key = f'session:{session_id}'
# Pipeline: GET + EXPIRE in one round trip
pipe = redis_client.pipeline()
pipe.get(session_key)
pipe.expire(session_key, ttl_seconds) # Reset TTL on access
results = pipe.execute()
data = results[0]
if data:
return json.loads(data)
return None # Session expired or not foundKoszyk zakupowy w Redis
Koszyk zakupowy w sklepie internetowym doskonale nadaje się do przechowywania w Redis. Każdy koszyk jest przechowywany jako Redis Hash, w którym pole oznacza SKU produktu, a wartość — jego ilość. Operacje na hashach, takie jak HINCRBY i HDEL, umożliwiają atomowe aktualizacje bez pobierania i ponownego zapisywania całego koszyka. W połączeniu z TTL-em (pozwalającym wygaszać porzucone koszyki po 24 godzinach) Redis zapewnia szybki i trwały magazyn koszyków bez narzutu związanego z używaniem relacyjnej bazy danych przy każdym zdarzeniu dodania produktu do koszyka.
# Shopping cart operations using Redis Hash
cart_key = f'cart:{user_id}'
# Add item (or increase quantity)
# HINCRBY cart:user42 SKU-001 2
redis_client.hincrby(cart_key, 'SKU-001', 2)
# Remove item
# HDEL cart:user42 SKU-001
redis_client.hdel(cart_key, 'SKU-001')
# Get all items in cart
# HGETALL cart:user42
cart = redis_client.hgetall(cart_key) # {b'SKU-001': b'2', b'SKU-002': b'1'}
# Set TTL for cart abandonment (24 hours)
redis_client.expire(cart_key, 86400)Architektura tabel wyników
Tabela wyników działająca w czasie rzeczywistym to klasyczny przypadek użycia Redis, możliwy dzięki uporządkowanym zbiorom (ZSET). Każdy wpis gracza ma wynik, a uporządkowany zbiór przez cały czas utrzymuje elementy w kolejności rosnących wyników. Zapytania dotyczące tabeli wyników (pierwszych N graczy, pozycji gracza, graczy z określonego zakresu wyników) mają złożoność O(log n) lub O(log n + m) — są niezwykle szybkie nawet w przypadku milionów graczy. Uporządkowane zbiory Redis stanowią podstawę wielu funkcji rankingowych w grach, aplikacjach fitness i serwisach społecznościowych, bez potrzeby korzystania ze złożonych zapytań do bazy danych ani ponownego obliczania pozycji przy każdym wyświetleniu strony.
# Real-time leaderboard with Redis Sorted Set
# Add or update a player's score
# ZADD game:weekly:leaderboard 15750 'player:alice'
redis_client.zadd('game:weekly:leaderboard', {'player:alice': 15750})
# Increment score (atomic)
# ZINCRBY game:weekly:leaderboard 500 'player:alice'
redis_client.zincrby('game:weekly:leaderboard', 500, 'player:alice')
# Get top 10 players (highest scores first)
# ZREVRANGE game:weekly:leaderboard 0 9 WITHSCORES
top_10 = redis_client.zrevrange('game:weekly:leaderboard', 0, 9, withscores=True)Pozycja gracza i pobliscy gracze
Dwie popularne funkcje tabel wyników, wykraczające poza „pokaż 10 najlepszych”, to pokazanie pozycji gracza oraz pokazanie graczy znajdujących się w pobliżu danego gracza. Obie są łatwe do zrealizowania za pomocą uporządkowanych zbiorów Redis. ZREVRANK zwraca pozycję gracza liczoną od zera, w kolejności malejących wyników. Aby wyświetlić 5 graczy powyżej i poniżej danego gracza, należy pobrać jego pozycję, a następnie użyć ZREVRANGE od pozycji rank-5 do rank+5. W ten sposób spersonalizowany widok tabeli wyników można uzyskać za pomocą dwóch poleceń Redis — bez potrzeby korzystania ze złożonych funkcji okienkowych SQL.
# Get Alice's rank (0-indexed, so add 1 for display)
# ZREVRANK game:weekly:leaderboard 'player:alice'
rank = redis_client.zrevrank('game:weekly:leaderboard', 'player:alice')
print(f'Alice is rank #{rank + 1}')
# Get 5 players above and below Alice
start = max(0, rank - 5)
end = rank + 5
nearby = redis_client.zrevrange(
'game:weekly:leaderboard', start, end, withscores=True
)
print('Players near Alice:', nearby)Ograniczanie liczby żądań za pomocą Redis
Ograniczanie liczby żądań (ograniczanie liczby żądań, które klient może wysłać w określonym przedziale czasu) to kolejny wartościowy przypadek użycia Redis. Algorytm przesuwnego okna wykorzystuje uporządkowany zbiór, w którym każdy element jest sygnaturą czasową żądania. Przy każdym żądaniu należy usunąć elementy starsze niż okno, policzyć pozostałe elementy, odrzucić żądanie, jeśli ich liczba przekracza limit, a następnie dodać nową sygnaturę czasową. Umożliwia to precyzyjne ograniczanie liczby żądań w przesuwnym oknie z dokładnością do milisekundy — znacznie dokładniej niż liczniki stałych okien i bez obciążania bazy danych.
# Sliding window rate limiter (100 requests per 60 seconds)
import time
def is_rate_limited(user_id, redis_client, limit=100, window_seconds=60):
key = f'ratelimit:{user_id}'
now = time.time()
window_start = now - window_seconds
pipe = redis_client.pipeline()
pipe.zremrangebyscore(key, '-inf', window_start) # Remove old
pipe.zcard(key) # Count current
pipe.zadd(key, {str(now): now}) # Add this request
pipe.expire(key, window_seconds)
results = pipe.execute()
request_count = results[1]
return request_count >= limit # True = rate limitedBlokady rozproszone za pomocą Redis
Blokady rozproszone koordynują wyłączny dostęp do współdzielonego zasobu między wieloma serwerami aplikacji. Polecenie SET key value NX EX ttl w Redis zapewnia atomowe uzyskiwanie blokady — ustawia klucz tylko wtedy, gdy ten nie istnieje (NX = Not eXists), oraz ustawia TTL, aby zapobiec zakleszczeniom w przypadku awarii właściciela blokady. Po zakończeniu operacji właściciel usuwa klucz. Algorytm Redlock (wykorzystujący wiele węzłów Redis do uzyskania kworum) zapewnia bardziej niezawodną blokadę rozproszoną, ale zwiększa złożoność rozwiązania. W większości przypadków wystarczająca jest blokada na pojedynczym węźle Redis.
# Distributed lock with Redis SET NX EX
import uuid
def acquire_lock(redis_client, resource, ttl_seconds=30):
lock_id = str(uuid.uuid4()) # Unique ID to identify this lock holder
key = f'lock:{resource}'
acquired = redis_client.set(key, lock_id, nx=True, ex=ttl_seconds)
return lock_id if acquired else None
def release_lock(redis_client, resource, lock_id):
key = f'lock:{resource}'
# Only delete if we still own the lock (Lua script for atomicity)
lua = 'if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end'
redis_client.eval(lua, 1, key, lock_id)Przechowywanie sesji: ElastiCache a DynamoDB
Zarówno ElastiCache Redis, jak i DynamoDB mogą przechowywać dane sesji, ale wiążą się z innymi kompromisami. ElastiCache Redis: opóźnienie rzędu mikrosekund, dane przechowywane w pamięci (ulotne, jeśli nie włączono trwałości), prostszy model danych, wymagany VPC. DynamoDB: opóźnienie rzędu pojedynczych milisekund (DAX może dorównać Redis), usługa w pełni zarządzana, bez konieczności utrzymywania klastra, domyślnie trwała, globalnie dostępna dzięki Global Tables, bezserwerowa z przepustowością na żądanie. Na egzaminie SAA-C03: jeśli pytanie podkreśla opóźnienie rzędu mikrosekund lub złożone operacje w pamięci, należy wybrać Redis. Jeśli podkreśla trwałość, bezserwerowość lub globalną skalę, warto rozważyć DynamoDB.
Indeksowanie geoprzestrzenne za pomocą Redis
Redis ma wbudowany geoprzestrzenny typ danych (polecenia GEO), który przechowuje współrzędne szerokości i długości geograficznej oraz umożliwia wyszukiwanie według odległości. Za pomocą GEOADD, GEODIST i GEORADIUS (obecnie GEOSEARCH w Redis 6.2) można znaleźć wszystkie lokalizacje w określonym promieniu od danego punktu w czasie O(n + log n). Przykładowe zastosowania: wyszukiwanie pobliskich kierowców (współdzielenie przejazdów), restauracji w promieniu 5 km oraz sortowanie wyników wyszukiwania według odległości. Pozwala to uniknąć korzystania z oddzielnej bazy geoprzestrzennej i utrzymywać zapytania lokalizacyjne z szybkością operacji w pamięci.
# Store driver locations
# GEOADD drivers 13.361389 38.115556 'driver:001'
# GEOADD drivers 15.087269 37.502669 'driver:002'
# Find all drivers within 10 km of a point
# GEOSEARCH drivers FROMLONLAT 13.5 38.1 BYRADIUS 10 km ASC COUNT 5 WITHCOORD
# Result: sorted list of driver IDs within 10 km with coordinatesHyperLogLog do zliczania unikalnych użytkowników
HyperLogLog to probabilistyczna struktura danych, która szacuje liczbę unikalnych elementów w zbiorze, wykorzystując stałą ilość pamięci (12 KB w Redis), niezależnie od liczby dodanych unikalnych elementów. Zapewnia standardowy błąd wynoszący około 0,81%. Za pomocą PFADD można dodawać elementy, a za pomocą PFCOUNT pobierać oszacowanie. Jest to idealne rozwiązanie do zliczania unikalnych użytkowników aktywnych dziennie, unikalnych wyświetleń stron lub unikalnych adresów IP, gdy dokładne wartości nie są wymagane, a oszczędne wykorzystanie pamięci ma znaczenie. Przechowywanie milionów unikalnych identyfikatorów użytkowników jako zbioru Redis zajęłoby gigabajty pamięci, natomiast HyperLogLog wykorzystuje 12 KB.
# Count unique daily visitors using HyperLogLog
date = '2024-01-15'
hll_key = f'unique_visitors:{date}'
# Track a visitor (PFADD is idempotent for the same user)
# PFADD unique_visitors:2024-01-15 'user:12345'
redis_client.pfadd(hll_key, 'user:12345')
redis_client.pfadd(hll_key, 'user:67890')
redis_client.pfadd(hll_key, 'user:12345') # Duplicate — not counted again
# Get estimated unique visitor count
# PFCOUNT unique_visitors:2024-01-15
count = redis_client.pfcount(hll_key)
print(f'Unique visitors today (estimate): {count}')Szybki test
Sprawdź swoją znajomość zagadnień z egzaminu AWS Solutions Architect (SAA-C03) omówionych w tej lekcji.
Podsumowanie lekcji
W tej lekcji nauczyli się Państwo, że: przechowywanie sesji w Redis umożliwia bezstanowe skalowanie horyzontalne, zapewniając wszystkim instancjom dostęp do współdzielonego stanu sesji z opóźnieniem poniżej milisekundy; uporządkowane zbiory Redis obsługują tabele wyników działające w czasie rzeczywistym, oferując zapytania o pozycję o złożoności O(log n); a wyspecjalizowane typy danych Redis (HyperLogLog do zliczania unikalnych elementów, GEO do wyszukiwania według odległości oraz blokady rozproszone) skutecznie rozwiązują typowe problemy architektoniczne. To kończy kurs Caching with ElastiCache — następnym tematem będą architektury wysokiej dostępności i odporne na awarie.
Często zadawane pytania
Czy lekcja „Przechowywanie sesji i wzorce tabel wyników” jest bezpłatna?
Tak — pełny tekst „Przechowywanie sesji i wzorce tabel wyników” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AWS Solutions Architect, przejdź na CoddyKit PRO. Kurs AWS Solutions Architect zawiera 4 lekcji w sumie.
Co nauczysz się w „Przechowywanie sesji i wzorce tabel wyników”?
Używać ElastiCache do odciążania serwerów aplikacji z przechowywania stanu sesji HTTP oraz implementować tablice wyników w czasie rzeczywistym za pomocą uporządkowanych zbiorów Redis. Ćwiczysz AWS Solutions Architect z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AWS Solutions Architect?
Nie wymagamy żadnego doświadczenia. AWS Solutions Architect w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Przechowywanie sesji i wzorce tabel wyników”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AWS Solutions Architect?
Tak. Każda lekcja AWS Solutions Architect zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Redis a Memcached: wybór właściwego silnika
- Grupy replikacji Redis ElastiCache i tryb klastra
- Strategie buforowania: lazy loading i write-through
- Przechowywanie sesji i wzorce tabel wyników