Batch vs streaming: фундаментальный сдвиг
Большинство дата-инженеров приходят в streaming с опытом batch — Spark, Airflow, dbt, Hive. Технически batch и streaming могут решать одни и те же задачи. Но ментальный сдвиг между ними настолько фундаментален, что простой “перевод” batch-кода в streaming почти всегда приводит к боли в production. Этот урок — про этот сдвиг.
К концу урока вы будете уметь различать, когда задача требует streaming, а когда вы делаете streaming просто потому, что это модно. Эта различимость важнее любого API.
Batch как мышление: данные конечны
В batch-обработке данные — это bounded dataset: конечный набор, который полностью существует в момент запуска job. Spark читает Parquet-файлы за вчерашний день, Hive обрабатывает таблицу. Job стартует, читает всё, преобразует, пишет, завершается.
DAG Scheduler в Spark: stages и barriersИз этого следуют ключевые свойства batch:
- Конечность — мы знаем границу датасета. SUM(amount) даст определённое число. ORDER BY работает — мы можем отсортировать всё в памяти.
- Повторяемость — запуск job завтра на тех же данных даст тот же результат. Это критично для regression testing и аудита.
- Латентность измеряется часами или сутками — batch-job стартует по cron, читает накопленные за период данные, выдаёт результат к утру.
- Полный набор инструментов SQL — JOIN, GROUP BY, window functions, recursive CTEs работают без оговорок.
Batch — это то, как мы думаем о данных естественно. “Возьми все вчерашние транзакции, посчитай выручку по категориям” — естественное предложение. Тонкости появляются, когда мы говорим “посчитай выручку прямо сейчас, по мере прихода транзакций”.
Streaming как мышление: данные бесконечны
В stream processing данные — это unbounded stream: бесконечная последовательность событий, новые приходят в любой момент. Stream-job стартует и работает 24/7, обрабатывая события по мере их появления.
Из этого следуют принципиально другие свойства:
- Бесконечность — мы никогда не знаем “всё”. SUM(amount) растёт всё время; ORDER BY невозможен в общем виде (мы не можем отсортировать бесконечность).
- Время становится first-class — события приходят с timestamp; время “сейчас” отличается от времени “когда событие произошло”; обработка должна понимать оба.
- Латентность измеряется секундами или миллисекундами — событие пришло, через 100 мс уже отражено в дашборде.
- State становится явным — где batch держит intermediate result в shuffle файлах, streaming держит его в state backend на много дней.
Stream processing — это другая парадигма. Все привычные операции (JOIN, GROUP BY, агрегация) переосмыслены: они работают на потоке, и их результат — тоже поток (continuous query).
Главная разница: bounded vs unbounded
Read Parquet
Источник: статический файл, таблица. Spark или Hive читают весь файл/таблицу как RDD/DataFrame. Размер данных известен заранее.Map
Map: применить функцию к каждой строке. В batch — параллельно по partitions, но всё происходит в течение одного job execution. После завершения map начинается shuffle.Group + Sum
Group by + Aggregate: shuffle перераспределяет данные по ключу, затем агрегация. В batch — после этого этапа агрегаты ПОЛНЫЕ и финальные. Сохраняем в файл и выходим.Write Parquet
Sink: записать финальный результат в Parquet, в таблицу. Job завершается. Все ресурсы (executors) освобождаются.Read Kafka
Источник: Kafka, Pulsar, Kinesis. События приходят бесконечно. Каждое событие обрабатывается отдельно, не дожидаясь следующих. Source постоянно poll'ит брокер.Map
Map: применяется к каждому событию по одному, ПО МЕРЕ ПРИХОДА. Событие 1 уже на этапе Group, событие 2 на Map — pipelined parallelism.Group + Sum (stateful)
Group + Sum: ОБНОВЛЯЕМЫЙ агрегат в state. Каждое новое событие апдейтит state и эмитит новое значение в down-stream. Финального результата НЕТ — он continuous.Write Kafka
Sink: пишет апдейты continuously. Каждое изменение агрегата эмитится. Sink — Kafka, JDBC, файл, dashboard. Job НЕ завершается, продолжает работать.Это не косметическое различие. Это два разных режима выполнения:
В batch DAG операторы выполняются последовательно по этапам (stages). Сначала весь Map, потом весь Shuffle, потом весь Aggregate. Между этапами — barrier: следующий этап стартует, когда предыдущий полностью завершён. В Spark это видно как этапы в Web UI: Stage 1, Stage 2, Stage 3.
В stream DAG операторы выполняются одновременно (pipelined). Источник поставляет события в Map, Map в Group, Group в Sink — все одновременно, конвейером. Событие N может быть в Group в то время, как событие N+1 ещё в Source. Это даёт streaming низкую end-to-end latency.
Где batch падает: примеры реальных задач
Несколько задач, которые batch решает “технически”, но streaming делает несоизмеримо лучше:
Real-time fraud detection. Транзакция пришла -> за 100 мс надо принять решение, fraud или нет. Batch-job, который раз в час смотрит транзакции, опоздает на час — карты уже использованы, деньги ушли. Streaming с stateful pattern matching и lookup в внешний risk-score сервис делает это за секунды.
Live dashboards. CEO открыл dashboard и хочет видеть выручку за сегодня прямо сейчас. Batch-job, который собирает дневной snapshot к утру следующего дня, не помогает. Streaming continuously обновляет агрегаты, дашборд видит изменения в реальном времени.
CDC (Change Data Capture). Реляционная база меняется тысячи раз в секунду. Batch-job, который читает таблицу каждые 5 минут, страдает от schema drift, потери изменений между чтениями, и невозможности отследить delete-операции. Streaming читает binlog/WAL и точно фиксирует каждое изменение.
CDC-фундаментал: что такое Change Data CaptureML feature engineering для online prediction. Модель в production требует features прямо сейчас — “сколько раз пользователь кликнул за последние 5 минут”. Batch-job не подходит — features устаревают. Streaming материализует features в feature store в реальном времени.
Общий паттерн: задачи, где latency имеет ценность или где данные не имеют естественной границы по времени (например, событийные потоки), естественно решаются streaming. Задачи отчётности “за вчерашний день”, аналитики “за квартал” — это всё ещё batch territory.
Lambda и Kappa архитектуры
Когда компании впервые столкнулись с потребностью в real-time данных, они изобрели Lambda Architecture — параллельное использование batch и streaming.
Lambda и Kappa архитектуры — обзорLambda Architecture (Nathan Marz, 2011)
Event Source (Kafka)
Источник событий — обычно Kafka или другой immutable log. Все события сохраняются для обоих layers одинаково.Batch Layer
Batch Layer: ежедневный pre-computed pre-aggregates. Spark/Hive обрабатывает накопленные за день данные, пишет в Hive table или Parquet. Latency: сутки. Точность: 100% (полные данные).Speed Layer
Speed Layer: real-time стрим, дающий приближённые real-time агрегаты. Storm/Spark Streaming/Flink. Latency: секунды. Точность: приближённая (без late events, без перерасчётов).Serving Layer
Serving Layer: объединяет batch и speed результаты. Запрос: 'дай агрегат за сегодня' — Cassandra/HBase отдаёт batch-результат за вчера + speed-результат за сегодня.Идея: batch layer даёт точные результаты с задержкой суток; speed layer даёт быстрые приближённые для текущего дня. Serving layer объединяет их.
Боль Lambda: поддерживать ДВЕ pipelines с разной логикой. Каждая бизнес-логика реализована дважды: в Spark и в Storm. Изменение требования — править в двух местах. Тесты — раздельные. Bug fix в одном месте, забыли в другом — расхождение между batch и speed.
Kappa Architecture (Jay Kreps, 2014)
Jay Kreps (создатель Kafka) предложил: давайте уберём batch layer вовсе. Если stream processor умеет работать с историческими данными (replay из Kafka с offset=0), мы можем делать всё в одной streaming-pipeline.
Event Source (Kafka with long retention)
Источник: immutable log (Kafka). Retention — достаточно длинная, чтобы можно было replay для re-compute (часто месяцы или годы для критичных доменов).Stream Processor (Flink)
Stream Processor: Flink, Kafka Streams. Один pipeline для всей логики. Для re-compute — стартуем второй экземпляр job с offset=0, прогоняем всю историю, переключаемся.Serving Layer
Serving Layer: continuously обновляемые материализованные представления. Cassandra/Postgres/Elasticsearch. Никакой агрегации между batch и speed — всё считается одним pipeline.Преимущества Kappa:
- Одна pipeline — одна логика, один codebase.
- Re-compute через replay — если правим логику, стартуем job v2 с offset=0, прогоняем историю, переключаем serving layer.
- Меньше operational complexity — один тип системы (streaming) вместо двух.
Kappa стала возможной благодаря Kafka (длинная retention) и Flink (надёжный stateful streaming с exactly-once). В 2026 году большинство новых архитектур начинают с Kappa, а Lambda сохраняется в legacy системах.
Kappa не значит “никогда не используем batch”. Batch остаётся для аналитики “вчерашних данных”, ad-hoc запросов через Trino/Presto, training ML-моделей. Kappa — это про operational pipelines, которые обслуживают live систему.
Когда выбирать batch, когда streaming
Не пытайтесь делать streaming везде. Это сложнее, требует больше дисциплины, и для многих задач избыточно.
Выбирайте batch, когда:
- Latency требования — часы или сутки. “Отчёт за вчерашний день к 9:00 утра” — batch.
- Данные имеют естественные границы. Месячный счёт за услуги — batch по концу месяца.
- Логика сложна и активно меняется. Batch проще отлаживать на конечном датасете.
- Точность критически важна, late events недопустимы. Финансовая отчётность для регулятора — batch с полной сверкой.
- Стоимость инфраструктуры важнее latency. Batch дешевле, потому что использует ресурсы только во время run.
Выбирайте streaming, когда:
- Latency требования — секунды или минуты. Live dashboards, real-time recommendations.
- Данные не имеют естественных границ. Event streams (clicks, transactions, sensor data) — streaming natural.
- Нужно реагировать на события (fraud detection, alerting). Batch не подходит — слишком поздно.
- Нужна материализация always-fresh state. Feature stores для online ML.
- CDC — streaming единственный корректный путь (нельзя терять delete-операции).
Часто правильный ответ — оба. Streaming для operational pipelines, batch для аналитики и backfills. Это и есть современная архитектура — Kappa для operational, batch для analytical, всё read’ит из одного immutable log (Kafka, Iceberg).
Попробуй сам
Возьмите 3 задачи из вашей текущей или прошлой работы:
- Какие из них вы решаете batch’ем? Подумайте — если бы latency требования были секунды, как бы это изменилось?
- Какие из них вы решаете streaming’ом? Подумайте — действительно ли latency настолько критична, или это unnecessary complexity?
- Найдите пример Lambda Architecture в legacy системе вашей компании. Можно ли его упростить до Kappa? Что мешает?
Ответы записывайте — мы будем возвращаться к этим примерам по ходу курса.