Kafka consumer groups на собеседовании Data Engineer
Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.
Содержание:
Зачем спрашивают на собесе DE
Kafka — стандарт стриминга. Consumer groups — основа распределённого consume'а. На собесе DE: «как работает rebalance», «зачем consumer group», «exactly-once vs at-least-once». Senior — тонкости static membership, incremental cooperative rebalancing, idempotent producers.
Главная боль без понимания — DE настроил consumer без понимания offset-коммита, при перезапуске обработали 1М сообщений дважды. Ловят на проде через неделю.
Что такое consumer group
Consumer group — набор consumers с общим group.id. Каждая partition топика читается ровно одним consumer'ом группы.
Topic "events" partitions [0, 1, 2, 3]
Consumer Group "ETL"
├─ Consumer A → partitions [0, 1]
└─ Consumer B → partitions [2, 3]Если consumer'ов больше, чем partitions — лишние простаивают. Параллелизм consumer'а ограничен числом partitions.
Свойства:
- Несколько групп могут читать тот же топик независимо (pub-sub).
- Внутри группы — конкурентное чтение (queue).
- Offsets хранятся per-group — каждая группа имеет свой прогресс.
- Group coordinator (брокер) управляет membership и assignment.
Offsets и commit
Offset — позиция consumer в partition. После обработки сообщения consumer коммитит offset, чтобы не читать его снова.
Виды commit:
1. Auto-commit (enable.auto.commit=true):
auto.commit.interval.ms=5000 # коммит каждые 5 секПросто. Опасно — может закоммитить до обработки. Если процесс упал — потеряли сообщения, или обработали повторно.
2. Manual commit:
consumer.poll(timeout_ms=1000)
for msg in messages:
process(msg)
consumer.commit_sync() # явный commit после обработкиКорректнее. Гарантия at-least-once (не теряем).
3. Per-partition manual commit: для тонкого контроля при error recovery.
4. Transactional commit: с idempotent producer + transactions — exactly-once в рамках Kafka.
Rebalance и его проблемы
Когда происходит:
- Consumer присоединяется или уходит из группы.
- Меняется число partitions.
session.timeout.msистёк (consumer не слал heartbeat).
Что происходит:
- Group coordinator (брокер) триггерит rebalance.
- Все consumers останавливают чтение.
- Coordinator пересчитывает assignment.
- Consumers получают новый набор partitions, начинают читать.
Stop-the-world. Во время rebalance вся группа стоит — обработка сообщений приостанавливается.
Проблемы:
- При большой группе rebalance занимает секунды-минуты.
- Если rebalance частые (флапающий consumer) — пропускная способность падает.
- Network shouldn't drop — иначе постоянные rebalance.
Решения в Kafka 2.4+:
- Static membership (
group.instance.id). Consumer держит persistent ID. Перезапуск — не вызывает rebalance, если consumer вернулся вsession.timeout.ms. - Incremental cooperative rebalancing (Kafka 2.4+). Не stop-the-world — partitions перераспределяются инкрементально.
Partition assignment стратегии
partition.assignment.strategy:
- RangeAssignor (default до 2.4) — сортирует partitions, делит на consumers по диапазонам. Может давать перекос на разных топиках в одной группе.
- RoundRobinAssignor — round-robin распределение. Равномерно.
- StickyAssignor — сохраняет назначения через rebalance — минимум переключений.
- CooperativeStickyAssignor (Kafka 2.4+) — sticky + cooperative. Дефолт в новых версиях.
В 2026 — CooperativeStickyAssignor рекомендуется для новых проектов.
Семантика доставки
At-most-once. Commit до обработки.
msg = poll()
commit()
process(msg) # если упало — сообщение потеряноИспользуется редко — данные дороже, чем дубликаты.
At-least-once. Commit после обработки.
msg = poll()
process(msg)
commit() # если упало после process до commit — повтор обработкиДефолт в большинстве пайплайнов. Получатель должен быть идемпотентен.
Exactly-once (EOS).
Несколько вариантов:
Within Kafka (read from Kafka → write to Kafka) — transactional producer + read isolated. С 0.11+. Гарантия в рамках Kafka.
Kafka → external sink — exactly-once требует двухфазного commit или idempotent receiver. Например, sink в Postgres с
INSERT ... ON CONFLICT— at-least-once + idempotency = effectively exactly-once.Spark Structured Streaming + Kafka — Spark может быть exactly-once при правильном sink (Delta Lake, idempotent JDBC).
Idempotent producer. enable.idempotence=true — гарантирует, что producer retry не создаст дубликат на брокере.
Lag и мониторинг
Consumer lag = latest_offset - committed_offset. Сколько сообщений отстал consumer от хвоста топика.
Метрики для мониторинга:
- Lag по partition / по group.
- Throughput (messages/sec).
- Rebalance count.
- Heartbeat success rate.
Tooling:
kafka-consumer-groups.sh --describe— встроенная.- Burrow / Kafka Lag Exporter / Cruise Control — production-grade.
- Confluent Control Center.
Что делать при росте lag:
- Добавить consumer'ов (если partitions > consumers).
- Увеличить
max.poll.records. - Оптимизировать processing (бутылочное горлышко часто в downstream).
- Параллелизовать processing внутри consumer'а (но не
max.poll.interval.msissue). - Reshard topic (увеличить число partitions).
Частые ошибки
Auto-commit + долгое processing. Auto-commit может закоммитить, пока process ещё идёт — при падении не восстановим.
max.poll.interval.ms слишком маленький. Если processing превышает interval — consumer выкидывается из группы → rebalance → дубликат обработки.
Один partition на топик. Параллелизма consumer нет — узкое место. На production — минимум 3-6 partitions, но можно сотни.
Не фиксировать group.id. Без group.id consumer работает в anonymous mode (с временным id) — нет offset commit. На рестарт — читает с начала или с конца.
Изменять число partitions топика. Существующие сообщения пере-распределяются по partition'ам через hash key — порядок ломается. Партиционирование — design-time decision.
Не использовать idempotent producer. Без него retry → дубликаты на брокере.
enable.auto.commit=true для критичных пайплайнов. Лучше manual commit с явным контролем.
Игнорировать lag. Lag растёт = consumer не справляется. Если 1 час lag в реалтайм-пайплайне — продукт ломается.
Использовать одинаковый group.id для разных пайплайнов. Они начнут конкурировать за partitions. Каждый pipeline = свой group.
Связанные темы
- Kafka на собесе DE
- Kafka на собесе SA
- CDC и Debezium на собесе DE
- Идемпотентность пайплайна для DE
- Подготовка к собесу Data Engineer
FAQ
Сколько partitions оптимально?
Зависит от throughput. Эмпирика: один partition держит 10-50 МБ/сек. Для 500 MB/sec — 10-50 partitions. Total partitions per cluster — обычно до десятков тысяч.
Можно ли разделить partition между двумя consumers одной группы?
Нет. Один partition — один consumer в группе. Это by-design (sequential ordering within partition).
Что если consumer падает посреди commit?
Транзакционный manual commit — атомарен (как и любой broker write). Если commit ack не дошёл до consumer — повторный poll вернёт те же сообщения, обработка дублируется. Идемпотентность downstream спасает.
acks=all для consumer есть?
acks — параметр producer, не consumer. У consumer — параметры fetch (fetch.min.bytes, fetch.max.wait.ms).
Как читать с начала топика?
auto.offset.reset=earliest (если нет committed offset) или consumer.seek_to_beginning() явно.
Это официальная информация?
Нет. Статья основана на документации Apache Kafka 3.x и Confluent.
Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.