Материал учебного циклаОткрытый материал

Redis: как работает быстрое хранилище данных в памяти

Введение в Redis: ключи, структуры данных, TTL, кэширование, eviction, транзакции, Pub/Sub, Streams, persistence, репликация и подключение к FastAPI.

Инфраструктура и DevOps #FastAPI #Pub/Sub #Redis #Streams #TTL #базы данных #кэш
Учебный цикл Основы API и backend-разработки Материал 6 из 8

Redis: как работает быстрое хранилище данных в памяти

PostgreSQL хорошо хранит связанные данные, поддерживает транзакции и выполняет сложные запросы. Но не каждую краткоживущую операцию выгодно каждый раз выполнять через основную базу.

Примеры:

  • получить часто запрашиваемый профиль;
  • сохранить сессию на 30 минут;
  • ограничить число запросов;
  • увеличить счётчик;
  • выдать одноразовый код;
  • хранить состояние задачи;
  • передать событие worker-процессу;
  • составить рейтинг.

Для таких задач часто используют Redis.

Redis - сервер структур данных, который в основном работает с данными в оперативной памяти и предоставляет команды для разных типов значений.

Ключ
-
структура данных
-
атомарная команда

Redis часто называют key-value database, но значением может быть не только строка. Redis поддерживает hashes, lists, sets, sorted sets, streams, JSON, географические индексы, вероятностные структуры и векторные наборы.

Redis может использоваться как:

  • кэш;
  • хранилище сессий;
  • счётчик;
  • rate limiter;
  • очередь или журнал событий;
  • хранилище быстрых временных состояний.

Обычно он дополняет PostgreSQL, а не заменяет его.

PostgreSQL: как устроена реляционная база данных


Клиент-серверная модель

Redis является отдельным сервером.

К нему подключаются:

  • FastAPI;
  • worker;
  • n8n;
  • CLI;
  • другие сервисы.

Стандартный адрес:

redis://localhost:6379/0
localhost - адрес сервера

6379 - стандартный порт

0 - номер логической базы

Логические базы Redis разделяют ключи по номеру, но не являются полноценными независимыми базами с отдельными схемами и правами. В серьёзной архитектуре чаще используют отдельные экземпляры для разных задач.


Почему Redis быстрый

Фраза «Redis быстрый, потому что работает в памяти» верна только частично.

Скорость обеспечивают:

  • хранение рабочего набора в RAM;
  • оптимизированные структуры данных;
  • простой сетевой протокол;
  • атомарные серверные команды;
  • отсутствие тяжёлого SQL-планирования для обычных операций.

Но память ограничена и дороже диска. Поэтому Redis требует контроля:

  • размера данных;
  • TTL;
  • политики вытеснения;
  • persistence;
  • числа ключей;
  • размера отдельных значений.

Ключи

Ключ идентифицирует значение.

session:7f23a
user:42
rate_limit:ip:192.0.2.1
order:105:status

Удобная схема именования:

область:объект:идентификатор:поле

Команды:

SET user:42:name "Jaguar"
GET user:42:name
DEL user:42:name
EXISTS user:42:name
TYPE user:42:name

Двоеточие не имеет специального значения для Redis. Это соглашение для читаемости.

Ключи тоже занимают память, поэтому слишком длинные имена при миллионах записей создают заметные расходы.


Основные структуры данных

Strings

String - последовательность байтов.

Она может хранить:

  • текст;
  • число;
  • JSON-строку;
  • бинарные данные;
  • сериализованный объект.
SET page:views 100
INCR page:views

INCR атомарно увеличивает значение. Два клиента не потеряют обновление из-за одновременного чтения и записи.

Другие команды:

INCRBY
DECR
APPEND
MGET
MSET

Hashes

Hash хранит поля одного объекта.

HSET user:42 name "Jaguar" age 25 city "Moscow"
HGET user:42 name
HGETALL user:42

Hash похож на плоский словарь:

{
  "name": "Jaguar",
  "age": "25",
  "city": "Moscow"
}

Он удобен для небольших объектов, если не нужны SQL-связи и сложные запросы.


Lists

List - упорядоченная последовательность строк.

LPUSH tasks task_1
LPUSH tasks task_2
RPOP tasks

Можно использовать как:

  • очередь;
  • стек;
  • список последних событий.

Для надёжной обработки сообщений, подтверждений и нескольких consumer чаще лучше использовать Streams или специализированный брокер.


Sets

Set хранит уникальные строки без порядка.

SADD article:42:tags python api fastapi
SISMEMBER article:42:tags python
SMEMBERS article:42:tags

Поддерживает операции:

SINTER
SUNION
SDIFF

Примеры:

  • уникальные посетители;
  • роли пользователя;
  • теги;
  • пересечение интересов.

Sorted sets

Sorted set хранит уникальные элементы и числовой score.

ZADD leaderboard 1500 user_42
ZADD leaderboard 1700 user_17
ZREVRANGE leaderboard 0 9 WITHSCORES

Подходит для:

  • рейтингов;
  • приоритетных очередей;
  • временных шкал;
  • диапазонов по числовому значению.

Streams

Stream - добавляемый в конец журнал записей.

XADD orders * order_id 105 status created

Redis создаёт уникальный ID.

Чтение:

XRANGE orders - +

Streams поддерживают consumer groups:

один поток
-
несколько consumer
-
распределение сообщений
-
pending entries
-
ACK после обработки

Они подходят для:

  • событий;
  • телеметрии;
  • очередей между сервисами;
  • уведомлений;
  • журналов действий.

Pub/Sub

Издатель отправляет сообщение в канал:

PUBLISH notifications "order created"

Подписчик слушает:

SUBSCRIBE notifications

Pub/Sub использует семантику at-most-once:

подписчик подключён
-
сообщение получено

подписчик отключён
-
сообщение потеряно

Сообщения не сохраняются как очередь.

Если требуется история, подтверждение и повторное чтение, лучше использовать Streams.


JSON, временные ряды и векторы

Современный Redis поддерживает дополнительные структуры:

  • JSON;
  • time series;
  • probabilistic data types;
  • geospatial;
  • vector sets и векторный поиск.

Они полезны для:

  • RAG;
  • семантического поиска;
  • телеметрии;
  • approximate counting;
  • географических запросов.

Но Redis стоит добавлять не из-за модного названия структуры, а из-за требований к скорости, обновлению и эксплуатации.


TTL

TTL - время жизни ключа.

SET reset_code:user_42 "834921" EX 300

Ключ существует 300 секунд.

Проверка:

TTL reset_code:user_42

Добавить срок к существующему ключу:

EXPIRE session:abc 1800

Применения:

  • сессии;
  • одноразовые коды;
  • кэш;
  • временные блокировки;
  • rate limiting;
  • промежуточные результаты.

После исчезновения ключа приложение должно корректно обработать cache miss или окончание срока.


Кэширование

Кэш хранит копию данных, чтобы не выполнять дорогую операцию повторно.

Шаблон cache-aside:

1. Приложение запрашивает Redis.
2. Если ключ найден, возвращает значение.
3. Если ключа нет, читает PostgreSQL.
4. Сохраняет результат в Redis с TTL.
5. Возвращает клиенту.

Псевдокод:

cached = redis.get(key)

if cached is not None:
    return deserialize(cached)

value = database.load(user_id)

redis.set(key, serialize(value), ex=300)

return value

Cache hit

Данные найдены.

Cache miss

Ключ отсутствует, и приложение обращается к источнику истины.

Инвалидация

После изменения исходных данных старый кэш нужно:

  • удалить;
  • обновить;
  • дождаться TTL.

Сложность кэша находится не в записи, а в поддержании допустимой актуальности.


Проблемы кэширования

Stale data

Redis хранит старую копию после изменения PostgreSQL.

Cache stampede

Популярный ключ истёк, и множество запросов одновременно пошло в базу.

Защита:

  • случайный разброс TTL;
  • lock на пересчёт;
  • предварительное обновление;
  • stale-while-revalidate.

Cache penetration

Запрашиваются несуществующие объекты, и каждый miss попадает в базу.

Иногда отрицательный результат кэшируют на короткое время.

Cache avalanche

Множество ключей истекает одновременно.


Eviction policy

При достижении лимита памяти Redis должен решить, что делать.

Политики включают:

noeviction - не удалять ключи, отклонять новые записи

allkeys-lru - удалять давно неиспользуемые ключи

allkeys-lfu - удалять редко используемые ключи

allkeys-random - удалять случайные ключи

volatile-* - выбирать только ключи с TTL

TTL и eviction различаются:

TTL - удаление по времени

Eviction - удаление из-за нехватки памяти

Для кэша eviction ожидаем. Для критичных данных автоматическое удаление может быть недопустимо.


Атомарность команд

Одна команда Redis выполняется атомарно относительно других команд.

INCR counter

безопаснее последовательности:

GET counter
↓
увеличить в приложении
↓
SET counter

Вторая схема создаёт гонку.

Полезные атомарные команды:

SET key value NX EX 30
HINCRBY
ZINCRBY

При проектировании лучше искать готовую серверную операцию вместо чтения и обратной записи.


MULTI, EXEC и WATCH

MULTI
INCR account:1
DECR account:2
EXEC

После MULTI команды ставятся в очередь. EXEC выполняет их последовательно.

Но это не SQL-транзакция:

  • нет обычного rollback после ошибки команды;
  • нет уровней изоляции PostgreSQL;
  • для optimistic locking применяется WATCH.
WATCH balance
GET balance
MULTI
SET balance 100
EXEC

Если отслеживаемый ключ изменился, клиент должен повторить операцию.


Lua-скрипты

Если несколько команд должны выполняться как единая операция, можно использовать Lua.

EVAL "
local current = redis.call('GET', KEYS[1])
if not current then
  return 0
end
redis.call('DEL', KEYS[1])
return 1
" 1 lock:task

Скрипт выполняется атомарно. Но долгий скрипт блокирует обработку других команд, поэтому он должен быть коротким.


Временная блокировка

SET lock:report worker_17 NX PX 30000
NX - установить только если ключа нет

PX 30000 - TTL 30 секунд

Значение должно быть уникальным токеном владельца.

Освобождать lock нужно только при совпадении токена. Обычный DEL может удалить блокировку, уже полученную другим worker после истечения старого TTL.

Distributed lock требует анализа сбоев и не должен восприниматься как простая замена транзакции PostgreSQL.


Rate limiting

Простой fixed window может использовать:

ключ - rate:user:42:2026-08-01T12:45

INCR - увеличить счётчик

EXPIRE - удалить после окна

Для согласованности INCR и EXPIRE лучше объединить атомарным скриптом.

Другие алгоритмы:

  • sliding window;
  • token bucket;
  • leaky bucket.

Persistence

Redis может сохранять данные на диск.

RDB

Создаёт снимки набора данных.

Плюсы:

  • компактность;
  • быстрое восстановление.

Риск:

  • изменения после последнего снимка могут потеряться.

AOF

Записывает операции изменения в append-only log.

Плюс:

  • меньший возможный интервал потери при подходящей fsync policy.

Минусы:

  • дополнительные записи;
  • более тяжёлый журнал.

RDB + AOF

Оба механизма можно сочетать.

No persistence

Подходит для полностью восстанавливаемого кэша.

Выбор зависит от роли Redis:

кэш - данные можно восстановить

критичное состояние - нужны persistence, backup и анализ гарантий

Репликация и высокая доступность

Redis поддерживает primary-replica репликацию.

primary - принимает записи

replica - асинхронно получает изменения

Асинхронность означает, что при сбое часть последних записей может не успеть попасть на replica.

Sentinel помогает обнаруживать сбой и выбирать новый primary.

Redis Cluster распределяет ключи между узлами и масштабирует объём и нагрузку.

Эти режимы требуют отдельного проектирования failover, клиентов и persistence.


Redis и FastAPI

Асинхронный клиент можно создать один раз при запуске приложения.

from contextlib import asynccontextmanager

from fastapi import FastAPI
from redis.asyncio import Redis


@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.redis = Redis.from_url(
        "redis://localhost:6379/0",
        decode_responses=True,
    )

    yield

    await app.state.redis.aclose()


app = FastAPI(lifespan=lifespan)

Endpoint:

@app.get("/counter")
async def counter():
    value = await app.state.redis.incr(
        "requests:counter"
    )

    return {"value": value}

Клиент переиспользует пул соединений. Создавать новый клиент на каждый запрос не нужно.


Сессии

Redis подходит для серверных сессий.

cookie - непрозрачный session_id

Redis key - session:<id>

value - user_id, роли, metadata

TTL - срок жизни

Нужно учитывать защищённый cookie, logout, продление TTL и чувствительность данных.


Мониторинг

Полезно отслеживать:

  • использованную память;
  • число ключей;
  • hit rate;
  • evicted keys;
  • expired keys;
  • latency;
  • число клиентов;
  • replication lag;
  • ошибки persistence;
  • размер Streams и pending entries.

Команды:

INFO
MEMORY USAGE key
SLOWLOG GET
CLIENT LIST

KEYS * нельзя бездумно запускать на большом production-наборе. Для постепенного обхода используют SCAN.


Redis и PostgreSQL

Вопрос PostgreSQL Redis
Основная модель Таблицы и связи Ключи и структуры
Хранение Диск и буферный кэш RAM с опциональной persistence
Запросы SQL, JOIN, агрегации Команды по ключам
Транзакции ACID Атомарные команды, MULTI/EXEC, Lua
Типичная роль Источник истины Кэш, сессии, счётчики, события
TTL Не центральная функция Встроенная возможность

Типичная архитектура:

PostgreSQL - долговременные связанные данные

Redis - быстрые временные и производные данные

Типичные ошибки

  • использовать Redis как бесконечную память;
  • хранить критичные данные без persistence;
  • считать Pub/Sub надёжной очередью;
  • применять KEYS в production;
  • хранить огромные значения;
  • не ставить TTL временным данным;
  • смешивать кэш, сессии и критичную очередь без изоляции;
  • освобождать lock обычным DEL;
  • считать replica резервной копией;
  • добавлять Redis без измеренной проблемы.

Итог

Redis - сервер структур данных в памяти.

key - имя записи

data type - string, hash, list, set, sorted set, stream и другие

TTL - время жизни

eviction - удаление при нехватке памяти

persistence - RDB и AOF

replication - копирование на replica

Redis особенно полезен, когда задача выражается одной атомарной командой:

INCR - счётчик

SADD - уникальное множество

ZADD - рейтинг

SET NX EX - временный захват

XADD - событие в stream

После понимания PostgreSQL и Redis статья про Docker становится логичным продолжением: читатель уже знает, какие сервисы запускаются, почему PostgreSQL нужен volume и зачем Redis нужны лимиты памяти, TTL и сеть.

Продолжить

  1. 01 PostgreSQL: как устроена реляционная база данных Введение в PostgreSQL: сервер и клиент, таблицы, ключи, связи, SQL, ограничения, JOIN, транзакции, индексы, JSONB, роли, резервные копии и подключение из FastAPI.
  2. 02 Docker: как контейнеризировать приложение и управлять его окружением Подробное введение в Docker: образы, контейнеры, Dockerfile, слои, порты, volumes, networks, Compose, health checks, безопасность, отладка и контейнеризация FastAPI.

Обратная связь

Материал был полезен?

Нашли ошибку или неточность? Сообщить →