0Pricing
AWS Solutions Architect · Урок

Хранение сеансов и шаблоны таблиц лидеров

Используйте ElastiCache, чтобы вынести состояние HTTP-сеансов с серверов приложения, и реализуйте таблицы лидеров в реальном времени с помощью сортированных множеств Redis.

«Хранение сеансов и шаблоны таблиц лидеров» — бесплатный урок AWS Solutions Architect на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AWS Solutions Architect, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AWS Solutions Architect содержит 4 уроков всего.

Проблема сеансов на стороне сервера

Традиционные веб-приложения хранят данные сеансов в памяти сервера. Это работает с одним сервером, но перестает работать при горизонтальном масштабировании: если последующий запрос пользователя направляется к другому экземпляру EC2, этот экземпляр ничего не знает о сеансе пользователя, и пользователь выходит из системы. Липкие сеансы (привязка сеанса в балансировщике нагрузки) частично решают проблему, но снижают эффективность балансировки нагрузки. Масштабируемое решение — перенести состояние сеансов в общее хранилище с низкой задержкой, доступное всем экземплярам. Именно это предоставляет ElastiCache Redis.

Redis для хранения сеансов

Хранение сеансов в Redis обеспечивает: чтение сеансов менее чем за миллисекунду на всех серверах приложения, встроенный TTL для автоматического истечения сеансов, атомарные обновления сеансов для предотвращения состояний гонки и возможность немедленно инвалидировать сеанс, удалив ключ. Приложение хранит ID сеанса в файле cookie; при каждом запросе оно ищет ID сеанса в Redis, чтобы получить данные сеанса. Все серверы приложения используют один и тот же Redis, поэтому любой сервер может обработать запрос любого пользователя.

# 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 None

TTL сеанса и скользящее истечение срока действия

Фиксированный TTL означает, что срок действия сеанса истекает через N секунд после создания независимо от активности. Скользящий TTL (продление срока действия при каждом обращении) удобнее для пользователя: срок действия сеанса истекает через N секунд после последнего обращения. В Redis реализуйте скользящий TTL, вызывая EXPIRE (или EXPIREAT) для ключа сеанса после каждого успешного чтения сеанса, чтобы сбрасывать отсчет до истечения срока действия. Это гарантирует, что активные пользователи не будут неожиданно выведены из системы, а неактивные сеансы будут автоматически удаляться, освобождая память.

# 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 found

Корзина покупок в Redis

Корзина покупок в электронной торговле — естественный сценарий для Redis. Каждая корзина хранится как хеш Redis, где полем является SKU товара, а значением — количество. Операции над хешами, такие как HINCRBY и HDEL, позволяют выполнять атомарные обновления без получения и повторной записи всей корзины. В сочетании с TTL (для удаления брошенных корзин через 24 часа) Redis предоставляет быстрое постоянное хранилище корзин без затрат на обращение к реляционной базе данных при каждом добавлении товара в корзину.

# 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)

Архитектура таблицы лидеров

Таблица лидеров в реальном времени — классический вариант использования Redis, реализуемый с помощью отсортированных множеств (ZSET). У каждой записи игрока есть счёт; отсортированное множество постоянно поддерживает элементы в порядке возрастания счёта. Запросы к таблице лидеров (топ-N игроков, ранг игрока, игроки в заданном диапазоне счёта) выполняются за O(log n) или O(log n + m) — чрезвычайно быстро даже для миллионов игроков. Отсортированные множества Redis лежат в основе многих игровых, фитнес- и социальных функций ранжирования, избавляя от необходимости выполнять сложные запросы к базе данных или пересчитывать ранг при каждом просмотре страницы.

# 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)

Ранг игрока и ближайшие игроки

Две распространённые функции таблиц лидеров помимо «показать топ-10» — это показать ранг игрока и показать игроков рядом с заданным игроком. Обе функции легко реализуются с помощью отсортированных множеств Redis. ZREVRANK возвращает ранг игрока с отсчётом от 0 при сортировке по убыванию счёта. Чтобы показать 5 игроков выше и ниже выбранного игрока, получите его ранг, а затем используйте ZREVRANGE от позиции ранг-5 до позиции ранг+5. Так Вы получите персонализированное представление таблицы лидеров всего двумя командами Redis — сложные оконные функции 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)

Ограничение частоты запросов с помощью Redis

Ограничение частоты запросов (ограничение количества запросов, которые клиент может отправить за определённый промежуток времени) — ещё один важный вариант использования Redis. В алгоритме скользящего окна используется отсортированное множество, где каждый элемент представляет собой временную метку запроса. При каждом запросе: удаляются элементы, которые старше окна, подсчитываются оставшиеся элементы, запрос отклоняется, если их количество превышает лимит, и добавляется новая временная метка. Это обеспечивает точное ограничение частоты запросов по скользящему окну с разрешением до миллисекунд — гораздо точнее, чем счётчики фиксированного окна, и без нагрузки на базу данных.

# 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 limited

Распределённые блокировки с помощью Redis

Распределённые блокировки координируют эксклюзивный доступ к общему ресурсу между несколькими серверами приложения. Команда Redis SET key value NX EX ttl обеспечивает атомарное получение блокировки: она устанавливает ключ, только если он не существует (NX = Not eXists), и задаёт TTL, чтобы предотвратить взаимные блокировки в случае сбоя владельца. После завершения операции владелец удаляет ключ. Алгоритм Redlock (использующий несколько узлов Redis для достижения кворума) обеспечивает более надёжную распределённую блокировку, хотя и усложняет систему. Для большинства вариантов использования достаточно блокировки на одном узле 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)

Хранение сеансов: ElastiCache и DynamoDB

И ElastiCache Redis, и DynamoDB могут хранить данные сеансов, но у них разные компромиссы. ElastiCache Redis: задержка в микросекундах, хранение в памяти (данные теряются при сбое, если не включено сохранение), более простая модель данных, требуется VPC. DynamoDB: задержка в несколько миллисекунд (DAX может сравниться с Redis), полностью управляемый сервис без необходимости обслуживать кластер, сохранность данных по умолчанию, глобальный доступ с помощью Global Tables, бессерверная работа с пропускной способностью по требованию. Для экзамена SAA-C03: если в вопросе подчёркиваются задержка в микросекундах или сложные операции в памяти, выбирайте Redis. Если акцент сделан на сохранности данных, бессерверной работе или глобальном масштабе, рассмотрите DynamoDB.

Геопространственная индексация с помощью Redis

В Redis есть встроенный геопространственный тип данных (команды GEO), который хранит координаты широты и долготы и поддерживает запросы по близости. С помощью GEOADD, GEODIST и GEORADIUS (в Redis 6.2 теперь GEOSEARCH) можно найти все местоположения в заданном радиусе от точки за O(n + log n). Варианты использования: поиск ближайших водителей (для сервисов совместных поездок), поиск ресторанов в радиусе 5 км, сортировка результатов поиска по расстоянию. Это позволяет обойтись без отдельной геопространственной базы данных и сохранять скорость запросов о местоположении на уровне операций в памяти.

# 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 coordinates

HyperLogLog для подсчёта уникальных посетителей

HyperLogLog — это вероятностная структура данных, которая оценивает количество уникальных элементов в множестве, используя фиксированный объём памяти (12 KB в Redis) независимо от количества добавленных уникальных элементов. Стандартная погрешность составляет примерно 0,81%. Используйте PFADD для добавления элементов и PFCOUNT для получения оценки. Это идеально подходит для подсчёта уникальных ежедневно активных пользователей, уникальных просмотров страниц или уникальных IP-адресов, когда точный подсчёт не требуется, а экономия памяти важна. Хранение миллионов уникальных идентификаторов пользователей в Set Redis заняло бы гигабайты, тогда как HyperLogLog использует 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}')

Быстрая проверка

Проверьте, насколько хорошо Вы усвоили концепции AWS Solutions Architect (SAA-C03) из этого урока.

Итоги урока

В этом уроке Вы узнали, что хранение сеансов в Redis обеспечивает горизонтальное масштабирование без состояния, предоставляя всем экземплярам доступ к общему состоянию сеансов с задержкой менее миллисекунды; отсортированные множества Redis поддерживают таблицы лидеров в реальном времени с запросами ранга за O(log n); а специализированные типы данных Redis (HyperLogLog для подсчёта уникальных элементов, GEO для поиска по близости, распределённые блокировки) эффективно решают распространённые архитектурные задачи. На этом завершается курс «Кэширование с ElastiCache» — далее мы рассмотрим высокую доступность и отказоустойчивые архитектуры.

Часто задаваемые вопросы

Урок «Хранение сеансов и шаблоны таблиц лидеров» бесплатный?

Да — полный текст урока «Хранение сеансов и шаблоны таблиц лидеров» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AWS Solutions Architect, подпишись на CoddyKit PRO. Курс AWS Solutions Architect содержит 4 уроков всего.

Чему я научусь в уроке «Хранение сеансов и шаблоны таблиц лидеров»?

Используйте ElastiCache, чтобы вынести состояние HTTP-сеансов с серверов приложения, и реализуйте таблицы лидеров в реальном времени с помощью сортированных множеств Redis. Ты практикуешь AWS Solutions Architect с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AWS Solutions Architect?

Предыдущий опыт не требуется. AWS Solutions Architect на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Хранение сеансов и шаблоны таблиц лидеров»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AWS Solutions Architect?

Да. Каждый урок AWS Solutions Architect включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Redis и Memcached: выбор подходящего механизма
  2. Группы репликации ElastiCache Redis и кластерный режим
  3. Стратегии кэширования: отложенная загрузка и сквозная запись
  4. Хранение сеансов и шаблоны таблиц лидеров
← Назад к AWS Solutions Architect