Big Data
Kafka: архитектура и паттерны
LinkedIn в 2011 году опубликовал Kafka с описанием задачи: перекачать активность 175 миллионов пользователей через десятки сервисов без потерь и дублей. Сегодня Kafka обрабатывает более 7 триллионов сообщений в сутки в одном только LinkedIn. В Uber, Netflix и Airbnb Kafka стал нервной системой всей real-time инфраструктуры. За этой масштабируемостью стоят четыре простых концепции: topics, partitions, consumer groups и delivery semantics.
- **LinkedIn** использует Kafka для activity stream: 7+ триллионов сообщений/сутки, единый поток данных между 1000+ микросервисов
- **Uber** строит real-time карту спроса через Kafka: GPS-события -> partition по геохешу -> аналитика без задержки
- **Confluent Cloud** управляет миллионами topic-ов для enterprise клиентов: Schema Registry + Kafka Connect превращают Kafka в центр интеграции данных
Topics и Partitions
LinkedIn в 2010 году столкнулся с проблемой: 300 сервисов хотят получать события действий пользователей. Прямые HTTP-вызовы между сервисами создают n² связей, система падает при любой перегрузке. Решение - **распределённый лог событий**. Продюсер пишет в один конец, консьюмеры читают независимо со своей скоростью. Так появился Kafka.
**Topic** - именованный поток сообщений, аналог таблицы в БД. Сообщения в topic-е хранятся как append-only лог: новые добавляются в конец, старые не удаляются сразу (retention по времени или размеру). Каждое сообщение имеет **offset** - монотонно возрастающий номер позиции в логе.
**Retention vs deletion:** Kafka не удаляет сообщения при прочтении (в отличие от RabbitMQ). Сообщения удаляются по retention policy (время или размер). Это позволяет нескольким независимым консьюмерам читать один topic, а также перечитывать исторические данные - критично для отладки и реплея событий.
Что происходит с сообщением в Kafka после того, как консьюмер его прочитал?
Partitions: горизонтальное масштабирование
Один лог - один поток записи. При 1 миллионе сообщений в секунду один брокер не справится. **Partition** - это решение: topic делится на N независимых логов, каждый на своём брокере. Продюсер пишет в разные partitions параллельно, консьюмеры читают параллельно.
Распределение по partitions определяется **ключом сообщения**: `partition = hash(key) % num_partitions`. Это гарантирует, что все сообщения с одним ключом (например, одним user_id) попадают в одну partition - а значит сохраняют порядок. Сообщения без ключа распределяются по round-robin.
**Replication и leader election:** каждая partition имеет одного leader-брокера и N-1 follower-ов. Все чтения и записи идут через leader. Если leader падает, Kafka автоматически выбирает нового из follower-ов (ISR - In-Sync Replicas). При replication_factor=3 кластер выдерживает отказ 2 из 3 брокеров без потери данных.
Kafka гарантирует порядок сообщений:
Consumer Groups: параллельное потребление
Topic с 12 partitions содержит события для аналитики: один сервис должен строить real-time дашборд, другой - записывать в Data Lake, третий - отправлять уведомления. Каждый из них должен получить все события. **Consumer Group** - механизм, позволяющий нескольким сервисам читать один topic независимо, и масштабировать каждый сервис горизонтально.
Внутри одной Consumer Group каждая partition назначается ровно одному консьюмеру - это обеспечивает параллельную обработку без дублирования. Разные Consumer Groups читают один и тот же topic независимо и получают все события.
**Rebalance:** когда консьюмер в группе падает или добавляется новый, Kafka проводит rebalance - перераспределение partitions между живыми консьюмерами. Во время rebalance обработка останавливается (stop-the-world). Kafka 2.4+ поддерживает Incremental Cooperative Rebalancing - только затронутые partitions перераспределяются, остальные продолжают работу.
Topic имеет 6 partitions. Consumer Group содержит 8 консьюмеров. Сколько консьюмеров будут активно читать данные?
Exactly-Once Semantics
Платёжный сервис обрабатывает транзакцию через Kafka. Сообщение получено, база данных обновлена, но при коммите offset-а Kafka временно недоступен. Консьюмер перезапустится и обработает сообщение снова - и транзакция задвоится. Это проблема **доставки сообщений**: at-most-once (потери), at-least-once (дубликаты), exactly-once (идеально, но сложно).
**Kafka Streams** обеспечивает exactly-once семантику автоматически при `processing.guarantee=exactly_once_v2`. Под капотом: транзакционный продюсер + атомарный commit offset + запись результата в выходной topic. Без Kafka Streams exactly-once требует ручного управления транзакциями или идемпотентной бизнес-логики (проверка дедупликации по event_id в БД).
Достаточно включить enable_idempotence=True на продюсере для полного exactly-once
Идемпотентный продюсер устраняет дубликаты только на стороне записи в Kafka (producer retry). Для полного exactly-once нужна транзакция, объединяющая чтение, обработку и запись - иначе дубликаты возникнут на стороне консьюмера.
Идемпотентность продюсера и exactly-once доставка - разные уровни гарантий. Продюсер может надёжно записать сообщение ровно раз, но консьюмер всё равно обработает его дважды при перезапуске без транзакционного commit-а offset-а.
At-least-once семантика означает, что:
Kafka: архитектура и паттерны
- Topic - append-only лог событий; сообщения не удаляются при чтении, хранятся по retention policy
- Partition = единица масштабирования и параллелизма; порядок гарантирован только внутри partition
- Consumer Group: N консьюмеров делят partitions между собой; разные группы читают topic независимо
- At-least-once (commit после обработки) - стандарт; exactly-once требует транзакций или идемпотентной логики
- Идемпотентный продюсер устраняет дубли записи; полный exactly-once нужно обеспечивать транзакцией
Связанные темы
Kafka - центр real-time инфраструктуры Big Data:
- Stream Processing: Flink и Spark Streaming — Kafka как источник данных для потоковой обработки
- Data Lake архитектура — Kafka -> Delta Lake/Iceberg: стандартный паттерн загрузки событий
- Delta Lake, Iceberg, Hudi — Kafka Sink Connector для потоковой записи в table formats
Вопросы для размышления
- При каком условии увеличение числа partitions не ускорит обработку - и как это диагностировать?
- Почему exactly-once в Kafka не защищает от дублей, если консьюмер пишет результат во внешнюю БД без дополнительных гарантий?
- Как правильно выбрать ключ партиционирования: по user_id или по event_type - и от чего это зависит?