Батч обрабатывает ограниченный набор данных целиком: «все заказы за вчера». Стриминг обрабатывает неограниченный поток по мере поступления: «каждое событие через секунды после того, как оно произошло». Выбор между ними это компромисс между свежестью, сложностью и стоимостью.
Когда что
Батч подходит, когда свежесть в часы или сутки устраивает: финансовая отчётность, ежедневные витрины, обучение моделей. Проще писать, тестировать, пересчитывать; дешевле.
Стриминг нужен, когда ценность данных быстро падает: антифрод, мониторинг, персонализация в сессии, алерты, оперативные дашборды. Сложнее: состояние, порядок событий, опоздания, гарантии доставки, эксплуатация 24/7.
Частый компромисс: микробатчи каждые 1-15 минут (Spark Structured Streaming по умолчанию работает так) или частые батчи из оркестратора.
Инструменты: Kafka как транспорт и буфер, Apache Flink и Spark Structured Streaming для обработки, Kafka Streams как библиотека внутри сервиса, ClickHouse как приёмник с собственными движками для чтения из Kafka.
Время события и время обработки
- Event time: когда событие произошло (время клика на телефоне).
- Processing time: когда система его обработала.
Между ними бывает разрыв от миллисекунд до суток: телефон был без сети, продюсер ретраил, консьюмер лежал. Если считать «клики за минуту» по processing time, сбой консьюмера на 10 минут нарисует провал и затем пик, которых в реальности не было. Для корректной аналитики окна строят по event time.
Окна
- Tumbling (фиксированные): непересекающиеся окна одного размера: каждая минута, каждый час.
- Sliding/hopping (скользящие): окна размера W со сдвигом S < W: «за последние 10 минут, обновляется каждую минуту». Событие попадает в несколько окон.
- Session: окно на пользователя закрывается после паузы без событий.
Watermark и опоздавшие события
Если события приходят не по порядку, когда можно закрыть окно 10:00-10:01 и выдать результат? Watermark это оценка «события с временем меньше T больше не придут». Часто его считают как максимальное увиденное event time минус допустимую задержку:
watermark = max(event_time) - allowed_lateness
Окно закрывается, когда watermark переходит его конец. События, пришедшие после закрытия окна, называются опоздавшими. Варианты: отбросить (и посчитать в метрике), отправить в отдельный поток, обновить уже выданный результат (если приёмник умеет upsert).
Компромисс: большая задержка watermark даёт полноту, но результат выходит позже и больше состояния в памяти; маленькая даёт быстрые, но неполные результаты.
Состояние
Агрегации, джойны потоков, дедупликация требуют состояния: что уже видели, какие окна открыты. Состояние хранится локально (у Flink в RocksDB) и периодически сохраняется в чекпоинт, чтобы после падения продолжить с того же места. Важно ограничивать состояние по времени (TTL), иначе дедупликация «по всем event_id за всё время» съест всю память.
Гарантии обработки
- At-most-once: событие обработается не больше одного раза, может потеряться.
- At-least-once: не потеряется, но может обработаться повторно. Самый частый режим.
- Exactly-once: эффект как от ровно одной обработки. На практике достигается либо транзакционной связкой источник-обработка-приёмник (чекпоинты Flink и транзакции Kafka), либо at-least-once плюс идемпотентный приёмник (upsert по ключу, дедупликация по id события).
Lambda и Kappa
- Lambda: два контура: батч считает точно, стриминг быстро и приблизительно, результаты объединяются. Минус: две реализации одной логики, которые расходятся.
- Kappa: только стриминг; пересчёт истории делается повторным прочтением лога (Kafka с долгим хранением) тем же кодом.
Сейчас часто встречается упрощённый вариант: стриминг в lakehouse или ClickHouse плюс периодический батч-пересчёт последних дней для исправления опозданий.
Частые ошибки
- Окна по времени обработки, когда нужен бизнес-смысл по времени события.
- Нет политики для опоздавших событий: они молча теряются.
- Состояние без TTL растёт до падения.
- Exactly-once «из коробки» в рекламных слайдах вместо идемпотентного приёмника.
- Стриминг там, где хватило бы батча раз в час.
Что спросят на собесе
- Когда нужен стриминг, а когда батч или микробатч.
- Event time против processing time.
- Типы окон, watermark и опоздавшие события.
- At-least-once, at-most-once, exactly-once и как добиться последнего.
- Lambda против Kappa.