← назад к разделу

Redis — не просто хранилище строк «ключ → значение». Он предоставляет несколько встроенных структур данных, и правильный выбор между ними напрямую влияет на простоту кода и производительность.

Почему важно выбирать правильную структуру

Представьте: вы храните профиль пользователя в одной строке JSON. Чтобы обновить одно поле — возраст — вы читаете всю строку, разбираете JSON, меняете поле, сериализуете обратно и пишете снова. Это пять шагов вместо одного.

Hash позволяет обновить одно поле напрямую командой HSET user:42 age 31. Меньше кода, меньше трафика, меньше ошибок.

Каждая структура Redis решает свой класс задач. Разберём каждую.

String — строки и счётчики

String — базовый тип Redis. Значением может быть обычная строка, число, JSON или бинарный blob (например, сжатые данные). Максимальный размер — 512 МБ.

SET user:42:name "Алексей"
GET user:42:name          # → "Алексей"

SET page:views 0
INCR page:views           # → 1
INCR page:views           # → 2
INCRBY page:views 10      # → 12

Короткая формула: INCR / INCRBY — атомарные операции без риска гонки, удобны для счётчиков и ограничителей частоты запросов (rate limiter).

Типичные применения String:

  • кэш HTML-фрагмента или API-ответа;
  • счётчик просмотров, лайков;
  • токен сессии или одноразовый код подтверждения.

TTL и истечение ключей

Любой ключ Redis можно сделать временным через TTL (time to live). Ключ будет автоматически удалён по истечении срока.

SET session:abc123 "user_data"
EXPIRE session:abc123 3600    # истекает через 1 час

# установить TTL сразу при записи:
SET otp:phone:79001234567 "8814" EX 300   # живёт 300 секунд

TTL session:abc123            # → оставшиеся секунды (-2 = уже нет)
PERSIST session:abc123        # убрать TTL, сделать постоянным

Redis использует два механизма вытеснения просроченных ключей:

  • Passive expiration — ключ проверяется и удаляется в момент обращения к нему.
  • Active expiration — фоновый процесс периодически проходит по выборке ключей и удаляет просроченные.

Это значит, что просроченные ключи не исчезают мгновенно — они могут «жить» в памяти ещё несколько секунд до следующего фонового прохода.

Hash — объект по полям

Hash хранит набор пар «поле → значение» под одним ключом. Идеально подходит для объектов с множеством атрибутов.

HSET product:10 name "Ноутбук" price 89990 stock 15
HGET product:10 price          # → "89990"
HGETALL product:10             # → все поля и значения
HINCRBY product:10 stock -1    # уменьшить остаток на 1
HDEL product:10 stock          # удалить одно поле

Плюс Hash над JSON-строкой: вы меняете одно поле без чтения всего объекта. При большом числе полей это ощутимо.

Типичные применения Hash:

  • профиль пользователя (имя, email, роль, дата регистрации);
  • корзина покупок (товар → количество);
  • конфигурация приложения.

List — очереди и стеки

List — двусторонняя очередь строк. Поддерживает добавление и чтение с обоих концов.

LPUSH tasks "задача-1"       # добавить в начало
RPUSH tasks "задача-2"       # добавить в конец
LPOP tasks                   # взять из начала → "задача-1"
RPOP tasks                   # взять из конца → "задача-2"
LLEN tasks                   # длина списка
LRANGE tasks 0 9             # первые 10 элементов

Короткая формула: LPUSH + RPOP = очередь FIFO; LPUSH + LPOP = стек LIFO.

Блокирующие варианты BLPOP / BRPOP ожидают появления элемента — это простая замена брокеру для несложных сценариев:

BLPOP tasks 5   # ждать элемент до 5 секунд, потом вернуть nil

Типичные применения List:

  • очередь задач (фоновые job'ы);
  • лента последних событий (с LTRIM для ограничения длины);
  • история действий пользователя.

Set — уникальные элементы

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

SADD tags:post:5 "java" "redis" "backend"
SADD tags:post:5 "redis"         # дубликат — игнорируется
SMEMBERS tags:post:5             # → {"java", "redis", "backend"}
SISMEMBER tags:post:5 "java"     # → 1 (есть) / 0 (нет)
SCARD tags:post:5                # → 3 (размер множества)

# Операции над несколькими множествами:
SINTER tags:post:5 tags:post:7   # пересечение
SUNION tags:post:5 tags:post:7   # объединение
SDIFF  tags:post:5 tags:post:7   # разность

Типичные применения Set:

  • список уникальных посетителей страницы;
  • теги статьи;
  • список друзей или подписчиков (пересечение = общие друзья).

Sorted Set — рейтинги и диапазоны

Sorted Set похож на Set, но каждый элемент имеет числовой score (вес). Элементы хранятся отсортированными по score — это ключевое свойство.

ZADD leaderboard 1500 "alice"
ZADD leaderboard 2300 "bob"
ZADD leaderboard 1800 "carol"

ZRANGE leaderboard 0 -1 WITHSCORES   # все, от меньшего к большему
ZREVRANGE leaderboard 0 2            # топ-3, от большего к меньшему
ZSCORE leaderboard "alice"           # → "1500"
ZRANK leaderboard "alice"            # позиция (0-based) по возрастанию
ZREVRANK leaderboard "bob"           # позиция в обратном порядке → 0 (1-й)

ZINCRBY leaderboard 200 "alice"      # добавить 200 к score alice

Типичные применения Sorted Set:

  • таблица лидеров игры (score = очки);
  • очередь задач с приоритетом (score = приоритет или временная метка);
  • история событий с поиском по диапазону времени.

Bitmap, HyperLogLog и Streams — кратко

Эти структуры решают специфические задачи:

Bitmap — битовый массив поверх String. Каждый бит адресуется смещением. Используется для компактной записи булевых фактов по большому числу сущностей.

SETBIT active_users:2024-06-01 42 1   # пользователь 42 был активен
GETBIT active_users:2024-06-01 42     # → 1
BITCOUNT active_users:2024-06-01      # количество активных в этот день

HyperLogLog — вероятностная структура для подсчёта уникальных элементов. Занимает не более 12 КБ независимо от числа элементов, но даёт приближённый результат (погрешность ~0,81%).

PFADD visitors:2024-06-01 "user-1" "user-2" "user-3"
PFCOUNT visitors:2024-06-01   # приблизительное число уникальных

Streams — структура для потоков событий, близкая по модели к Kafka. Каждая запись имеет уникальный ID и набор полей. Поддерживает группы потребителей и чтение с подтверждением.

XADD events * action "click" user_id "42"   # добавить событие
XREAD COUNT 10 STREAMS events 0              # прочитать последние 10

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

Какую структуру выбрать

ЗадачаСтруктура
Кэш произвольного значения или счётчикString
Объект с именованными полямиHash
Очередь или стекList
Коллекция уникальных значенийSet
Рейтинг, очередь с приоритетомSorted Set
Активность миллионов сущностейBitmap
Количество уникальных (приблизительно)HyperLogLog
Поток событий с группами потребителейStreams

Коротко

  • Redis предоставляет семь встроенных структур данных — String, Hash, List, Set, Sorted Set, Bitmap, HyperLogLog, Streams.
  • String работает и как строка, и как атомарный счётчик (INCR/INCRBY).
  • Hash позволяет обновлять отдельные поля объекта без перезаписи всего значения.
  • List реализует очереди (FIFO) и стеки (LIFO); BLPOP делает очередь блокирующей.
  • Set хранит уникальные элементы и поддерживает операции пересечения, объединения, разности.
  • Sorted Set добавляет к Set числовой score — элементы всегда отсортированы.
  • Любой ключ можно сделать временным через EXPIRE — Redis удалит его автоматически.

Что почитать дальше

  • Основы Redis: как работает, зачем нужен
  • Паттерны кэширования: cache-aside, write-through, TTL
  • Redis за пределами кэша: очереди, блокировки, pub/sub