Kafka как хранилище
Kafka как хранилище — это использование распределённого журнала (log) для долгосрочного хранения событий, где сообщения не удаляются после прочтения, а сохраняются заданное время (retention) или до достижения лимита объёма. В отличие от традиционных брокеров (RabbitMQ), Kafka позволяет перечитывать историю, воспроизводить события с любого offset, использовать компактизацию (оставлять только последнее сообщение по ключу) и применять потоковую обработку (Kafka Streams, ksqlDB). Основные сценарии: event sourcing (источник истины — поток событий), CQRS, CDC (Change Data Capture), аудит, аналитика в реальном времени и восстановление состояния сервисов. Однако Kafka не заменяет базу данных: нет обновлений (только append), сложных запросов и JOIN без дополнительных инструментов, а retention ограничен — данные рано или поздно удаляются. Типичная архитектура: Kafka для истории изменений, база данных для текущего состояния.
Введение: Журнал, который никогда не выбрасывает черновики
Представьте, что вы ведёте дневник. Вы пишете каждый день. Вы можете перечитать записи за прошлый месяц, прошлый год, за всё время. Вы можете начать читать с любого места. Вы можете искать по датам. Дневник — это не просто “передача сообщений”. Это хранилище вашей истории.
Большинство брокеров сообщений (RabbitMQ, ActiveMQ) — как почтовый ящик. Вы отправили письмо, адресат прочитал — письмо исчезло. Они не хранят историю.
Kafka — другой подход. Это журнал (log). Сообщения не исчезают после прочтения. Они хранятся дни, недели, месяцы. Любой потребитель может прочитать любое сообщение в любое время. Kafka — это брокер, который одновременно является хранилищем данных.
Kafka как хранилище означает, что вы можете использовать её не только для передачи сообщений между сервисами, но и для долгосрочного хранения данных, для воспроизведения истории (replay), для анализа потоков данных. Вы можете перечитать события, которые произошли неделю назад. Вы можете подключить новый сервис и дать ему прочитать всю историю событий.
Для системного аналитика понимание Kafka как хранилища открывает новые возможности: event sourcing (источником истины становится поток событий), CQRS (разделение команд и запросов), аналитика на потоковых данных, замена классических баз данных в некоторых сценариях.
Kafka vs Традиционные брокеры
| Характеристика | Традиционный брокер (RabbitMQ) | Kafka |
|---|---|---|
| После прочтения | Сообщение удаляется | Сообщение остаётся |
| Повторное чтение | Невозможно | Можно (по offset) |
| Хранение | Временно (пока не доставлено) | Длительное (дни, месяцы) |
| История | Нет | Да |
| Основное применение | Очереди задач | Потоки событий, хранилище |
Какие данные можно хранить в Kafka
События (Events)
Всё, что произошло в системе, и важно сохранить.
Примеры событий:
- Пользователь создан
- Заказ оформлен
- Платёж прошёл
- Товар отгруженЖурналы изменений (Change Log)
Последовательность изменений состояния.
Журнал изменений пользователя:
offset 0: user_123, {name: "Иван", status: "active"}
offset 1: user_123, {name: "Иван", status: "blocked"}
offset 2: user_123, {name: "Иван", status: "deleted"}CDC (Change Data Capture)
Лог изменений из базы данных.
CDC поток из PostgreSQL:
- INSERT в таблицу users
- UPDATE в таблицу orders
- DELETE из таблицы sessionsАудитные логи
Кто, когда, что сделал.
Аудит действий администратора:
- admin: user_456 изменил роль
- admin: user_789 удалил заказОсобенности хранения в Kafka
Журнальная структура (Log)
Данные хранятся в виде последовательности сообщений. Каждое сообщение имеет смещение (offset) — порядковый номер внутри партиции.
Партиция 0:
offset 0: {user_id: 123, action: "login"}
offset 1: {user_id: 123, action: "search"}
offset 2: {user_id: 456, action: "login"}
offset 3: {user_id: 123, action: "logout"}Свойства:
| Свойство | Значение |
|---|---|
| Упорядоченность | Порядок сообщений гарантирован внутри партиции |
| Неизменяемость | Нельзя изменить или удалить сообщение (только по retention) |
| Детерминированный доступ | Чтение по offset (O(1)) |
Retention (Срок хранения)
Данные хранятся ограниченное время или до достижения лимита объёма.
retention.ms = 604800000 (7 дней)
retention.bytes = 1073741824 (1 ГБ на партицию)Что это значит для хранилища: Kafka не подходит для хранения данных “навсегда”. Данные рано или поздно удаляются. Для долгосрочного хранения нужна выгрузка в другие системы (S3, HDFS).
Компактизация (Compaction)
Оставляет только последнее сообщение для каждого ключа.
Исходный лог:
key: user_123 → {status: "active"}
key: user_123 → {status: "blocked"}
key: user_123 → {status: "deleted"}
После компактизации:
key: user_123 → {status: "deleted"} # только последнееКогда это полезно: Хранение текущего состояния. Таблицы в Kafka (KTable).
Сценарии использования Kafka как хранилища
1. Event Sourcing (Хранилище событий)
Все изменения состояния системы хранятся как последовательность событий. Текущее состояние вычисляется путём воспроизведения событий.
События пользователя:
- Событие: UserCreated (id=123, name="Иван")
- Событие: UserRenamed (id=123, new_name="Иван Петров")
- Событие: UserBlocked (id=123)
Текущее состояние (вычисленное):
- id=123, name="Иван Петров", status="blocked"Почему Kafka: Хранит события в порядке, можно воспроизвести с любого момента.
2. CQRS (Command Query Responsibility Segregation)
Команды (запись) идут в Kafka. Отдельные сервисы читают события и строят оптимизированные модели для чтения.
Команда: CreateOrder
→ Kafka (событие OrderCreated)
→ Сервис записи (обновляет источник истины)
→ Сервис чтения (обновляет денормализованную view)3. Замена базы данных для некоторых сценариев
Kafka может заменить базу данных, если:
- Вам нужна только последовательная запись (нет обновлений)
- Вам нужен доступ по первичному ключу (key-value)
- Вам не нужны сложные запросы (JOIN, агрегации)
Что нельзя делать с Kafka:
| Операция | Поддержка в Kafka |
|---|---|
| Обновление записи | Нет (только новая запись) |
| Удаление записи | Нет (только через компактизацию) |
| SELECT с условиями | Нет (только по ключу или по offset) |
| JOIN | Нет (только через Streams) |
4. Окно истории для новых сервисов
Новый сервис может прочитать всю историю событий и построить своё состояние.
Новый сервис аналитики:
1. Подключается к Kafka
2. Читает все события за последние 30 дней
3. Строит аналитические отчёты
4. Продолжает читать новые события5. Аналитика на потоковых данных
Обработка и анализ данных в реальном времени.
Kafka Streams:
- Подсчёт событий по окнам
- Агрегации
- Обогащение потоков
- Join потоков6. Резервное копирование и восстановление
Kafka хранит события. Если система обработки потеряла состояние, она может перечитать события и восстановиться.
Сервис упал:
1. Восстановился
2. Читает события с последнего сохранённого offset
3. Восстанавливает состояниеОграничения Kafka как хранилища
1. Нет случайного доступа по значению
Можно читать только по offset или по ключу (в компактифицированных топиках). Нельзя SELECT * FROM topic WHERE field = value.
Обход: Отдельный индекс в другой системе (Elasticsearch, Redis).
2. Нет обновлений
Нельзя изменить уже записанное сообщение. Только написать новое.
Обход: Компактизация для key-value сценариев.
3. Retention ограничен
Данные не хранятся вечно. Рано или поздно удаляются.
Обход: Выгрузка в долгосрочные хранилища (S3, HDFS).
4. Нет транзакций между топиками
Транзакции в Kafka работают в пределах одного топика. Нельзя атомарно обновить два топика.
5. Запросы сложнее, чем SQL
Нет SQL. Нужно писать код (Kafka Streams, ksqlDB, или свой потребитель).
Kafka vs Базы данных
| Характеристика | PostgreSQL | Kafka |
|---|---|---|
| Модель данных | Таблицы, строки, колонки | Журнал, ключ-значение |
| Обновление | UPDATE, DELETE | Только INSERT |
| Чтение | SELECT с условиями, JOIN | По offset, по ключу |
| История | Только текущее состояние (без аудита) | Полная история |
| Порядок | Не гарантирован | Гарантирован внутри партиции |
| Производительность записи | Тысячи TPS | Миллионы TPS |
| Производительность чтения | Быстро (с индексами) | Последовательно (быстро) |
| Retention | Вечно | Ограничен |
Практические примеры
Пример 1: Event Sourcing для корзины покупок
Топик: cart_events
События:
- ItemAdded (user=123, product=456, quantity=1)
- ItemAdded (user=123, product=789, quantity=2)
- ItemRemoved (user=123, product=456)
- CartCheckedOut (user=123)
Потребитель:
- Читает события для user=123
- Восстанавливает состояние корзины
- Применяет командыПример 2: CDC из PostgreSQL в Kafka
Исходная таблица: orders
Debezium (CDC):
- Ловит изменения в PostgreSQL
- Публикует в Kafka
Потребители:
- Сервис аналитики: считает метрики
- Сервис поиска: обновляет Elasticsearch
- Хранилище данных: загружает в ClickHouseПример 3: Хранилище последних состояний (KTable)
Топик: user_events (компактифицированный)
Kafka Streams:
- Строит KTable (последнее состояние по ключу)
- user_123: {status: "active", last_login: "2024-01-15"}
- user_456: {status: "blocked", last_login: "2024-01-10"}
Интерактивные запросы:
- Получить статус user_123 (через IQ)ksqlDB: SQL для Kafka
ksqlDB — это движок потоковой обработки с SQL-интерфейсом.
-- Создать поток из топика
CREATE STREAM user_events (
user_id INT,
action VARCHAR
) WITH (KAFKA_TOPIC='user_events', VALUE_FORMAT='JSON');
-- Подсчёт действий по пользователям
SELECT user_id, COUNT(*) FROM user_events
GROUP BY user_id
EMIT CHANGES;
-- Соединить два потока
SELECT * FROM user_events u
JOIN user_profiles p ON u.user_id = p.id;Когда использовать Kafka как хранилище
Подходит
| Сценарий | Почему |
|---|---|
| Событийная архитектура | Нужна история событий |
| Event Sourcing | Поток событий как источник истины |
| CDC | Лог изменений базы данных |
| Аналитика на потоковых данных | Обработка в реальном времени |
| Воспроизводимые вычисления | Можно пересчитать результат с начала |
| Аудит | Нужно знать, кто что делал |
Не подходит
| Сценарий | Почему |
|---|---|
| Традиционное CRUD-приложение | Нужны обновления, удаления, сложные запросы |
| Данные, которые нельзя потерять | Retention удаляет старые данные |
| Маленькие объёмы | Избыточно |
| Требуются JOIN с большим количеством таблиц | Kafka не оптимизирована для этого |
Распространённые ошибки
Ошибка 1: Kafka как единственное хранилище
Думают, что Kafka заменит PostgreSQL. Но в Kafka нет обновлений, нет сложных запросов, нет индексов.
Решение: Kafka для событий, PostgreSQL для текущего состояния.
Ошибка 2: Бесконечное хранение
Устанавливают бесконечный retention. Место кончается.
Решение: retention = 7-30 дней. Выгружать старые данные в S3/HDFS.
Ошибка 3: Использование Kafka для маленьких объёмов
Несколько событий в день. Kafka избыточна.
Решение: PostgreSQL с аудитом.
Ошибка 4: Попытка обновить сообщение
Пытаются изменить уже записанное сообщение.
Решение: Записать новое сообщение с тем же ключом, использовать компактизацию.
Ошибка 5: Игнорирование компактизации
Хранят историю изменений, но никогда не компактизируют. Место растёт.
Решение: Включить компактизацию для key-value сценариев.
Резюме
Kafka как хранилище — это использование журнальной структуры для хранения событий. Данные не удаляются после прочтения, хранятся определённое время.
Ключевые сценарии: event sourcing, CQRS, CDC, аналитика, аудит, восстановление состояния.
Ограничения: нет обновлений, нет сложных запросов, retention ограничен, нет SQL (без ksqlDB).
Компактизация позволяет хранить только последнее состояние (key-value хранилище).
Kafka не заменяет базы данных. Она дополняет их. База данных для текущего состояния, Kafka для истории изменений.
ksqlDB добавляет SQL-интерфейс для потоковой обработки.