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