Redis — хранилище данных в памяти

Redis — открытая in-memory база данных типа key-value, которая работает со скоростью порядка 100 000 запросов в секунду. Используется для кэширования, очередей, сессий и многих других задач.

Уровень: начальный 18 разделов ⏱ 2–3 часа

1. Что такое Redis

Redis (Remote Dictionary Server) — это ключ-значение хранилище, данные в котором хранятся преимущественно в оперативной памяти. Благодаря этому чтение и запись выполняются за микросекунды, что делает Redis одним из самых быстрых хранилищ данных.

Сравнение с реляционной БД

ХарактеристикаRedisPostgreSQL / MySQL
Скорость чтения~0.1 мс~1–10 мс
ХранениеОперативная память + дамп на дискДиск
СтруктураKey-value (без таблиц)Таблицы с SQL
Транзакции ACIDЧастичная поддержкаПолная
Объём данныхДо нескольких ГБ (в памяти)Терабайты

Типичные сценарии использования

2. Установка и запуск

Windows

Официального установщика нет. Используйте форк Memurai или скачайте бинарники с GitHub: github.com/tporadowski/redis.

redis-server --service-install
redis-server --service-start

Linux (Debian / Ubuntu)

sudo apt update
sudo apt install redis-server
sudo systemctl enable redis-server

macOS

brew install redis
brew services start redis

Проверка работоспособности

Запустите клиент и отправьте пинг:

redis-cli
127.0.0.1:6379> PING
PONG
Redis слушает порт 6379 по умолчанию. Убедитесь, что он не заблокирован файрволом.

3. Структура данных STRING

STRING — самая простая структура Redis. Значение — произвольная строка до 512 МБ.

SET user:1:name "Иван"
GET user:1:name

SET session:abc "active" EX 60
INCR page:views:home
INCRBY user:1:balance 500
DECR product:stock:42
КомандаОписание
SET key valueЗаписать значение
GET keyПолучить значение
SETEX key seconds valueЗаписать с TTL
INCR keyУвеличить на 1
DECR keyУменьшить на 1
APPEND key valueДописать в конец строки

4. LIST, SET, HASH, ZSET

LIST — двусторонняя очередь

LPUSH queue:tasks "task1" "task2" "task3"
RPUSH queue:tasks "task4"
LPOP queue:tasks
RPOP queue:tasks
LRANGE queue:tasks 0 -1
LLEN queue:tasks

SET — неупорядоченное множество

SADD tags:post:1 "python" "redis" "tutorial"
SMEMBERS tags:post:1
SISMEMBER tags:post:1 "redis"
SCARD tags:post:1
SREM tags:post:1 "tutorial"

HASH — ассоциативный массив полей

HSET user:1 name "Иван" age "30" email "ivan@mail.ru"
HGET user:1 name
HGETALL user:1
HINCRBY user:1 age 1
HDEL user:1 email

ZSET — отсортированное множество

ZADD leaderboard 1500 "player1" 2300 "player2" 1800 "player3"
ZRANGE leaderboard 0 -1 WITHSCORES
ZRANGEBYSCORE leaderboard 1600 2000
ZSCORE leaderboard "player2"
ZREM leaderboard "player1"
ZSET идеален для реализации лидербордов и приоритетных очередей.

5. TTL и кэширование

TTL (Time To Live) определяет, сколько секунд значение будет доступно. После истечения ключ удаляется автоматически.

SET cache:homepage "html content" EX 60
TTL cache:homepage
EXPIRE cache:homepage 120
PERSIST cache:homepage

Паттерн cache-aside

  1. Приложение запрашивает ключ.
  2. Если значение есть в Redis — возвращаем его (кэш-попадание).
  3. Если нет — читаем из БД, записываем в Redis с TTL и возвращаем.
  4. При обновлении данных в БД — удаляем ключ из Redis (инвалидация).
TTL для кэша должен быть достаточно длинным, чтобы снизить нагрузку на БД, но коротким, чтобы данные не устаревали критично.

6. Redis как кэш для веб-приложений

В реальных проектах Redis берёт на себя роль промежуточного кэша между приложением и основной базой данных. Это позволяет снизить нагрузку на БД в десятки раз для горячих данных.

Кэширование результатов запроса

def get_user_profile(user_id):
    cache_key = f"user:{user_id}:profile"
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)
    profile = db.query("SELECT * FROM users WHERE id=%s", user_id)
    redis_client.setex(cache_key, 300, json.dumps(profile))
    return profile

Инвалидация кэша

def update_user(user_id, data):
    db.execute("UPDATE users SET ... WHERE id=%s", user_id)
    redis_client.delete(f"user:{user_id}:profile")
Неправильная инвалидация приводит к отдаче устаревших данных. Всегда удаляйте или обновляйте кэш при записи в основную БД.

7. Redis из Python

Установка клиентской библиотеки:

pip install redis

Базовые операции

import redis
import json

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

r.set("greeting", "Привет, Redis!")
print(r.get("greeting"))

r.hset("user:42", mapping={"name": "Аня", "age": "25"})
print(r.hgetall("user:42"))

data = {"items": [1, 2, 3], "total": 3}
r.setex("cart:99", 600, json.dumps(data))
cached = json.loads(r.get("cart:99"))
print(cached)
Параметр decode_responses=True автоматически декодирует байты в строки UTF-8.

8. Redis из Java

Основная библиотека — Jedis. Добавьте зависимость в Maven:

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>5.1.0</version>
</dependency>

Пример использования

import redis.clients.jedis.Jedis;

Jedis jedis = new Jedis("localhost", 6379);

jedis.set("java:key", "Hello from Java");
String value = jedis.get("java:key");
System.out.println(value);

jedis.hset("user:7", "name", "Максим");
jedis.hset("user:7", "age", "28");
System.out.println(jedis.hgetAll("user:7"));

jedis.close();
Для продакшена используйте JedisPool или Lettuce (входит в Spring Boot) с пулом соединений.

9. Persistence: RDB и AOF

Redis хранит данные в RAM, но поддерживает два механизма сохранения на диск:

RDB — снимки (snaphots)

Redis периодически записывает полный дамп памяти в файл dump.rdb.

save 60 1000
save 300 10
save 3600 1

Пример: если за 60 секунд было изменено 1000 ключей — создать снимок.

AOF — журнал операций

Каждая записывающая команда логируется в файл appendonly.aof.

appendonly yes
appendfsync everysec
ПараметрRDBAOF
ВосстановлениеПоследний снимокПерезапуск всех команд
Размер файлаКомпактныйМожет быть большим
Производительность записиНагрузка в момент снимкаПостоянная
Потеря данныхДо последнего снимкаДо последней fsync
Отключайте persistence только если Redis используется исключительно как кэш и данные не критичны.

10. Redis в Docker

Быстрый запуск Redis одной командой:

docker run -d --name my-redis -p 6379:6379 redis:7

Подключение к контейнеру

docker exec -it my-redis redis-cli
127.0.0.1:6379> PING
PONG

docker-compose.yml для приложения

version: "3.9"
services:
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  redis-data:
Используйте redis:7-alpine — образ весит всего ~30 МБ.

11. Pub/Sub и репликация

Pub/Sub — система подписок

Redis позволяет создавать каналы для обмена сообщениями в реальном времени.

SUBSCRIBE notifications
PUBLISH notifications "Новый заказ #1234"

В приложении подписка обрабатывается асинхронно через listener. Pub/Sub удобен для уведомлений, чатов и real-time обновлений.

Репликация Master-Slave

Redis поддерживает асинхронную репликацию: master передаёт записи на один или несколько slave-серверов.

# На slave-сервере:
REPLICAOF 192.168.1.10 6379

# Проверка роли:
INFO replication
  1. Master обрабатывает все записи (запись).
  2. Slave получает данные асинхронно (чтение).
  3. При падении master можно назначить нового master через REPLICAOF NO ONE.
Для автоматического переключения master используйте Sentinel или Redis Cluster.

12. Redis Streams — надёжные очереди событий

Streams — лог-структура, появившаяся в Redis 5.0. В отличие от Pub/Sub, сообщения хранятся в памяти и могут быть прочитаны повторно: потребитель не теряет события, даже если временно отключился или упал.

XADD orders "*" product "42" qty "2"
XLEN orders
XRANGE orders - +

XREAD COUNT 10 STREAMS orders 0-0

Каждое сообщение получает монотонный ID вида 1679481234567-0. Группы потребителей распределяют нагрузку между воркерами: команда XREADGROUP выдаёт событие одному из участников, а обработка подтверждается через XACK. Неподтверждённые события остаются в списке pending и читаются заново после рестарта.

Fire-and-forget уведомления и метрики — это Pub/Sub, а надёжные рабочие очереди — Streams с группами потребителей. Не путайте эти два механизма.

13. Redis Cluster — горизонтальное масштабирование

Когда данных больше не помещается в память одной машины, на помощь приходит Redis Cluster: данные автоматически делятся по 16384 слотам между узлами, а клиент обращается к любому из них.

redis-cli --cluster create \
  192.168.1.10:7000 192.168.1.11:7000 \
  192.168.1.12:7000 --cluster-replicas 1

redis-cli -c -p 7000
CLUSTER INFO
CLUSTER SLOTS
Cluster использует только базу 0 и не поддерживает переименование ключей между слотами. Сначала спроектируйте схему ключей, потом масштабируйтесь.

14. Производительность и мониторинг

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

INFO memory
INFO commandstats
SLOWLOG GET 5
redis-cli --stat
redis-cli --bigkeys
МетрикаНа что смотреть
used_memory_rssреальное потребление RAM процессом
hit_rate / miss_rateдоля эффективных обращений к кэшу
evicted_keysвытеснение ключей при нехватке памяти
slowlogкоманды, работающие дольше порога (по умолчанию 10 мс)

Ключи-монстры размером в десятки мегабайт и команды KEYS * в проде запрещены: для перебора используйте SCAN. Для production-наблюдения данные из INFO собирают в Prometheus/Redis Exporter и выводят в Grafana.

Максимальной скорости достигайте правильной моделью данных (HASH вместо десятков ключей) и настройкой maxmemory-policy: allkeys-lru — для кэша, noeviction — для очередей и важных данных.

15. Redis Modules: RediSearch, RedisJSON и RedisBloom

Redis с версии 4.0 умеет расширяться модулями. Модуль — это библиотека на C, которая добавляет новые типы данных и команды. Крупные провайдеры (Redis Enterprise, Redis Cloud, некоторые облачные сборки) включают модули из коробки, поэтому знание их возможностей пригодится в реальных проектах.

МодульЧто добавляет
RediSearchполнотекстовый поиск и вторичные индексы
RedisJSONJSON-документы с вложенными полями
RedisBloomвероятностные структуры: Bloom, Cuckoo, Top-K
RedisTimeSeriesметрики и временные ряды с агрегацией
FT.CREATE idx:product ON HASH PREFIX 1 "product:" SCHEMA name TEXT price NUMERIC
FT.SEARCH idx:product "note*" LIMIT 0 5
JSON.SET user:5 $ '{"name":"Аня","age":25}'
BF.ADD emails "ivan@mail.ru"

RedisJSON хорош, когда структура ответа вложенная, а реляционная схема избыточна. RedisBloom экономит память при подсчёте уникальных значений и защищает кэш от пробития. Учитывайте, что каждый модуль занимает память и добавляет нагрузку на CPU, поэтому подключайте только нужные.

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

16. HyperLogLog и GEO — счётчики и гео-данные

HyperLogLog (HLL) — вероятностная структура для подсчёта уникальных элементов с фиксированной памятью около 12 КБ, независимо от числа значений. Погрешность ~0.81% — для аналитики «уникальные посетители» это приемлемо, а экономия памяти по сравнению с SET выглядит колоссальной.

PFADD visits:2026:09 "user:1" "user:2" "user:1"
PFCOUNT visits:2026:09
PFMERGE visits:month visits:2026:09 visits:2026:08

GEO — индекс гео-координат, хранящий широту и долготу в упорядоченном виде. Он нужен для «ближайшие кафе», «магазины в радиусе 5 км» и расчёта дистанций без внешних сервисов.

GEOADD shops 37.617 55.751 "кафе на Арбате"
GEOSEARCH shops FROMLONLAT 37.62 55.75 BYRADIUS 3 km ASC
GEODIST shops "кафе на Арбате" "кафе 2" km
HLL нельзя уменьшать и удалять отдельные элементы: если нужны точные значения или отказы — используйте SET. Оценивайте погрешность до внедрения в расчётные отчёты.
GEO-координаты кодируются компактно, но порядок аргументов всегда «долгота широта» — перепутаете, получите точки в океане.

17. Распределённые блокировки

Когда несколько серверов пишут в один ресурс (заказ, загрузка файла), блокировки на уровне приложения недостаточно. Redis раздаёт распределённые лок-и атомарной командой SET ... NX EX: ключ создаётся только если его ещё нет, и с TTL.

SET lock:order:42 "worker-7" NX EX 10
# true — блокировка получена, false — занято

if redis.call("get", KEYS[1]) == ARGV[1] then
  return redis.call("del", KEYS[1])
else
  return 0
end

Второй скрипт — корректное снятие блокировки через Lua: удаляем только свою метку (кто взял, тот и снимает), иначе после истечения TTL один процесс удалит блокировку другого.

Не используйте старые SETNX/EXPIRE двумя командами — между ними процесс может упасть. Команда SET key val NX EX атомарна.
Redlock снижает время сбоя за счёт нескольких мастеров, но цена — лишняя сложность. Для большинства сервисов достаточно одного мастера с корректным снятием блокировки.

18. Пробитие, сбой и лавина кэша

Кэш, настроенный как в разделе 6, ломается тремя классическими способами. Пробитие (penetration) — атака несуществующими ключами: каждый запрос проходит сквозь кэш к БД. Сбой (breakdown) — горячий ключ истёк, и десятки запросов одновременно идут в БД. Лавина (avalanche) — истекло много ключей разом, и БД захлёбывается.

УгрозаРешение
пробитиекэшировать отрицательный результат (null) или фильтр Блума
сбой ключа«лок за обновление» или single-flight
лавинаджиттер TTL + рандомная экспирация
# джиттер: не TTL=300, а TTL=300±random(60)
cache_key = f"user:{uid}:profile"
ttl = 300 + random.randint(-60, 60)
redis.setex(cache_key, ttl, data)
Индикатор, что вы в лавине: шип на графике БД в момент массовой экспирации. Вводите джиттер сразу при проектировании ключей.

19. Транзакции MULTI/EXEC и Lua-скрипты

Несколько команд можно выполнить как единый блок: MULTI ставит их в очередь, а EXEC выполняет все сразу. Пока идёт транзакция, другие клиенты не вклиниваются между командами — блок исполняется атомарно.

MULTI
INCR counter:visits
LPUSH log:visits "2026-09-19"
EXEC

Важное отличие от реляционных БД: Redis не делает откат (rollback). Если команда упадёт во время выполнения, остальные всё равно применятся. Поэтому перед операциями проверяйте типы значений, а ошибки обрабатывайте на клиенте.

Для атомарной логики с условиями используйте Lua: скрипт передаётся в EVAL и выполняется целиком, без прерывания другими запросами. Пример — списание со счёта только при достаточном остатке:

EVAL "local b = tonumber(redis.call('GET', KEYS[1]))
      if b < tonumber(ARGV[1]) then return 0 end
      redis.call('DECRBY', KEYS[1], ARGV[1])
      return b - ARGV[1]" 1 "account:42" 150
ПодходКогда брать
MULTI/EXECпакетное выполнение независимых команд
Lua-скриптусловия и зависимости между ключами, атомарный обмен
WATCHоптимистичная блокировка по версии ключа
Часто исполняемые скрипты загружайте через SCRIPT LOAD и вызывайте по SHA: трафик между клиентом и сервером уменьшается, а логика не дублируется по языкам.

20. Keyspace notifications: события Redis

Redis умеет публиковать события о ключах: создание, изменение, удаление и — самое полезное — истечение TTL. Это открывает путь к «отложенным действиям без очередей»: сброс кэша, холодный старт данных, напоминания.

# в redis.conf или через CONFIG SET
notify-keyspace-events Ex

События уходят в каналы Pub/Sub двух видов: __keyspace@0__:<ключ> (операция в сообщении) и __keyevent@0__:<операция> (имя ключа в сообщении). Подпишемся на истечение:

SUBSCRIBE __keyevent@0__:expired
# при истечении ключа прилетит: expired user:42:temp
КаналЧто в сообщении
__keyspace@0__:user:1операция: set, del, expired
__keyevent@0__:expiredимя истёкшего ключа
События доставляются «как есть» (fire-and-forget): если подписчик упал, события теряются навсегда. Для критичных задач используйте Streams с группами потребителей из раздела 12.
Популярный приём: временная пометка «сбросить популярный кэш через 10 минут» — задаёте ключ с TTL, а на событие expired чистите зависимые ключи.

21. Rate limiting: окна и вёдра

Ограничение частоты запросов — одна из самых частых задач Redis. Простейшая схема — фиксированное окно: инкремент счётчика и TTL на границу окна.

# до 100 запросов на ключ в минуту
val = INCR rate:user:42:minute
if val == 1: EXPIRE rate:user:42:minute 60
if val > 100: отклонить запрос

Фиксированное окно пропускает двойную нагрузку на стыке окон. Точнее — скользящее окно на ZSET: храним метки времени запросов, срезаем старые и считаем свежие.

ZREMRANGEBYSCORE rate:user:42 0 (now-60)
ZCARD rate:user:42
ZADD rate:user:42 now now
СхемаПамятьТочность
fixed window1 ключумеренная (стыки окон)
скользящий ZSETзапись на запросвысокая
token bucket (Lua)1–2 ключавысокая, стабильный «пульс»
Возвращайте X-RateLimit-Remaining и Retry-After — клиенты смогут вести себя вежливо (см. курс REST API).
Лимит по IP дорого обходится: мобильные пользователи меняют адрес, а за одним прокси сидят тысячи людей. Ограничивайте по токену или аккаунту, а грубый IP-лимит держите отдельно.

22. Безопасность Redis: ACL и сетевой контур

Redis по умолчанию отдаёт данные без пароля — на локальной машине это удобно, в проде губительно. Защита строится тремя линиями: сеть, аутентификация и права.

# redis.conf
bind 127.0.0.1 10.0.0.5
protected-mode yes
requirepass "длинный-случайный-пароль"

С версии 6.0 доступны ACL: отдельные пользователи со своими паролями и наборами команд. Приложению-кэшу не нужны FLUSHALL и чтение чужих ключей.

ACL SETUSER app on >секрет ~cache:* +@read +@write -@admin
ACL SETUSER readonly on >секрет -@all +get
requirepass защищает только пользователя «default». Если сеть не закрыта файрволом, любой подключившийся может шарить по ключам. Правильный порядок: firewall → bind → protected-mode → ACL.
Проверка после настройки: подключение под новым пользователем отдаёт PONG, под старым — отказ. Такую проверку легко автоматизировать в CI из курса Ansible.

Что дальше

Теперь ты знаешь основы Redis. Рекомендуемый следующий шаг — изучение Redis Streams для продвинутых очередей, Redis Cluster для масштабирования, а также интеграция Redis с веб-фреймворками (Django, Spring Boot) для построения высоконагруженных приложений.

← Вернуться к курсам