Redis Streams
Redis Streams — это структура данных в системе управления базами данных Redis, предназначенная для реализации модели «журнала» (log) и обеспечивающая упорядоченное, неизменяемое хранение записей с поддержкой подписки на события, групповой обработки и потребления с сохранением состояния (consumer groups). Streams объединяют свойства списков (lists), каналов Pub/Sub и упорядоченных множеств (sorted sets), позволяя создавать надёжные очереди сообщений, системы журналирования, потоковой аналитики (event streaming) и архитектуры событийно-ориентированных приложений.
История
Структура данных Streams была добавлена в Redis версии 5.0, выпущенной в октябре 2018 года. Разработка велась в рамках проекта Redis под руководством Сальваторе Санфилиппо (Salvatore Sanfilippo, известного как antirez) начиная с 2017 года. Основной мотивацией стало отсутствие в Redis встроенного, устойчивого к потерям данных механизма для передачи сообщений с полной семантикой «at-least-once» или «exactly-once» (хотя в Redis Streams строгая гарантия «exactly-once» не реализована на уровне протокола). Ранее для организации очередей использовались списки (BLPOP/BRPOP) или каналы Pub/Sub, но эти решения имели ограничения: списки не поддерживали множественных потребителей с независимыми позициями, а Pub/Sub не гарантировал доставку сообщений отключённым подписчикам. Streams стали ответом на потребность в более зрелой, похожей на Apache Kafka модели потоковой обработки данных.
Структура и основные понятия
Каждый поток (stream) в Redis представляет собой упорядоченную последовательность записей (entries). Каждая запись имеет уникальный идентификатор (ID), который автоматически генерируется сервером (по умолчанию — на основе текущей миллисекунды и порядкового номера внутри неё) либо может быть задан клиентом в формате <время-в-миллисекундах>-<порядковый-номер>. Каждая запись содержит одно или несколько пар ключ-значение (field-value), как в хешах.
Идентификаторы записей
Идентификаторы строго монотонно возрастают. Даже если два сообщения добавлены с одинаковой временной меткой, порядковый номер обеспечивает полный порядок. Идентификатор позволяет:
- выполнять точное позиционирование при чтении.
- получать все записи с ID, большим заданного (
XREADс параметром$— с начала,>— новые). - удалять записи по ID.
Основные команды
XADD— добавляет новую запись в поток.XREAD— читает записи из одного или нескольких потоков (с блокировкой или без).XRANGE/XREVRANGE— возвращает записи в заданном диапазоне ID по порядку или в обратном порядке.XDEL— удаляет запись по ID (мягкое удаление — память освобождается только при ручной чистке или по maxlen).XLEN— возвращает длину потока (количество записей).XTRIM— обрезает поток до заданного количества записей (стратегияMAXLEN) или до заданной длины (стратегияMINID— удаляет записи с ID меньше указанного).
Емкость и хранение
Потоки хранятся в оперативной памяти, как и все данные в Redis. Для экономии памяти и ограничения размера потока используется опция MAXLEN при добавлении записи или команда XTRIM. Redis может автоматически обрезать поток при превышении заданного размера (например, «~ 1000» — примерный, не строгий лимит, обеспечивающий компромисс между точностью и производительностью). Полностью сохранённые потоки поддерживают только мягкое удаление через XDEL, при этом записи физически удаляются только при обрезке (trim) или истечении TTL (которое не поддерживается для отдельных записей, только для всего ключа stream).
Группы потребителей (Consumer Groups)
Это ключевое нововведение Redis Streams, делающее их похожими на consumer group в Apache Kafka. Группа потребителей позволяет множеству процессов (потребителей) совместно читать один и тот же поток, при этом каждая запись доставляется только одному потребителю в группе. Каждый потребитель в группе хранит свою позицию (last delivered ID) и информацию о неподтверждённых записях.
Управление группами
XGROUP CREATE— создаёт группу для указанного потока. Можно указать начальный ID (0— начать с самого начала,$— только новые записи).XREADGROUP— читает записи из потока от имени группы. Потребитель получает записи, которые ещё не были переданы другим потребителям в группе.XACK— подтверждает, что запись успешно обработана. После подтверждения запись удаляется из списка «очень ожидающих» (pending entries list, PEL).XGROUP SETID— изменяет последний доставленный ID для группы.XGROUP DESTROY— удаляет группу.XGROUP DELCONSUMER— удаляет потребителя из группы (высвобождает его неподтверждённые записи).
Механизмы обработки ошибок
При сбое потребителя есть риск потерять сообщения, которые были доставлены, но не обработаны. Для этого в каждой группе ведётся список ожидающих записей (PEL). Потребитель может перечитать список неподтверждённых записей по команде XPENDING. Другой потребитель может «присвоить» себе эти записи с помощью XCLAIM, передав их старый ID и новый потребитель. Это позволяет реализовать повторную обработку (retry) и балансировку после сбоя.
Применение
Очереди сообщений
Redis Streams часто используются для построения надёжных очередей сообщений в микросервисной архитектуре. Возможность создания групп потребителей позволяет равномерно распределять нагрузку между обработчиками, а PEL гарантирует, что сообщение не будет потеряно даже при падении потребителя. В отличие от списков, Streams поддерживают «неблокирующее» чтение нескольких новых записей одновременно.
Системы событийного журналирования (event sourcing)
Упорядоченная неделимая запись событий — естественная модель для event sourcing. Каждое событие добавляется в поток, ипотека ID генерируется автоматически, что обеспечивает хронологический порядок. Потоки могут использоваться для сохранения истории действий пользователя, изменений состояния системы или аудита.
Уведомления в реальном времени
Блокирующий XREAD позволяет реализовать серверные push-уведомления (пул листьев) с низкой задержкой. В отличие от Pub/Sub, сообщения не теряются при отключении подписчика — он при следующем подключении может запросить пропущенные записи по ID.
Аналитика и мониторинг
Потоки могут быть временными буферами для потоков данных (логгов, метрик). Команды XRANGE и XREVRANGE позволяют эффективно извлекать записи за заданный временной интервал. Обрезка по MAXLEN предотвращает бесконечный рост объёма памяти.
Сравнение с другими механизмами Redis
| Механизм | Сохранение сообщений от отключаемых подписчиков | Несколько потребителей с независимыми позициями | Может быть персистентным | Потребление без подтверждения |
|---|---|---|---|---|
| Lists | Да (по чтению удаляются) | Нет (каждое сообщение читается одним потребителем) | Да | Да (ошибка потребителя — удаление) |
| Pub/Sub | Нет (сообщения не сохраняются) | Да (каждому подписчику своя копия) | Нет (только в памяти) | Да (не применяется) |
| Sorted Sets | Да (время выполнения) | Нет (эмуляция) | Да | Нет |
| Streams | Да (при группе — PEL) | Да (через группы потребителей) | Да | Да (ACK) |
Ограничения и особенности
- Гарантии доставки: Redis Streams обеспечивает гарантию «at-least-once» при использовании групп потребителей и подтверждений (XACK). Гарантия «exactly-once» не поддерживается стандартным API; её можно реализовать на стороне клиента идемпотентностью обработчиков.
- Персистентность: как и любые другие данные в Redis, потоки могут быть сохранены на диск с помощью снимков (RDB) или журналов (AOF). При сбое сервера возможна потеря небольшого количества последних несинхронизированных записей.
- Производительность: операции с потоками выполняются со сложностью O(1) или O(log N) для большинства команд. Однако обрезка потока (XTRIM) может быть ресурсоёмкой при больших объёмах.
- Отсутствие rebalancing: в отличие от Kafka, Redis не автоматически перераспределяет записи между потребителями в группе при добавлении/удалении потребителя. Разработчик должен реализовать механизм ручного присвоения записей через XCLAIM или создать свою логику.
- Размер сообщения: каждое поле и его значение представляются в памяти как строки. Для больших сообщений рекомендуется сжимать данные на стороне клиента.
Интересные факты
- Redis Streams вдохновлены Apache Kafka, но в отличие от Kafka не полагаются на дисковые сегменты и распределённое хранение; все данные находятся в оперативной памяти одного узла.
- Идентификаторы записей в формате
<миллисекунды>-<порядковый номер>позволяют использовать поток как генератор уникальных, упорядоченных во времени идентификаторов. - Существуют сторонние библиотеки (например,
RedisGears), которые расширяют функциональность Streams, добавляя возможности потоковой трансформации данных.
Источники
- Документация Redis: «Redis Streams» (официальная документация).
- Salvatore Sanfilippo. «Redis Streams: an introduction» (блог antirez, 2018).
- Redis Labs. «Redis Streams in Action» (техническое руководство).
- Официальный репозиторий Redis на GitHub (описание команды XADD, XREAD, XGROUP и др.).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →