Batch или streaming: выбираем по сроку бизнес-решения

Отдел продаж просит «данные в реальном времени». Инженеры обсуждают Kafka, потоковый Spark и постоянно работающий кластер. Но руководитель смотрит итоговую таблицу раз в день перед планёркой. Чтобы выбрать конвейер, сначала выясните, какое действие опоздает из-за старого результата. От этого зависят частота обработки и цена поддержки.

У каждого потребителя свой срок

Представим сеть магазинов. План закупок составляют к девяти утра по итогам вчерашнего дня. Пополнение полок проверяют каждые полчаса. Сигнал о недоступности кассы нужен сразу, пока посетитель ещё ждёт. Одно название «данные продаж» скрывает три разных продукта. Для первого подходит ежедневный расчёт, для второго стоит проверить частый пакетный запуск, третий требует отдельного оперативного пути.

Запишите для каждого результата момент решения, допустимый возраст исходных событий и поведение при опоздании. Если пополнение полки можно отложить до следующего обхода, один пропущенный запуск не равен остановке кассы. Это позволяет назначить разную надёжность и избежать общей дорогой архитектуры под самый строгий, но редкий сценарий.

Теперь посмотрите на источник. Если кассовая система присылает файлы раз в час, потоковая обработка после загрузки не обеспечит пятиминутную свежесть. Время от события до полезного результата включает задержку источника, ожидание запуска, обработку и публикацию. Сначала измеряют весь путь, затем сокращают его конкретную часть.

Проверим частоту на числах

В примере пополнение должно видеть события не старше пятнадцати минут. По согласованной верхней оценке источник задерживает отправку на четыре минуты, обработка занимает три, публикация — одну. Для периодического запуска берём худшее ожидание полного интервала: событие может прийти сразу после начала предыдущего расчёта. Сравним три расписания.

source_delay = 4
processing = 3
publication = 1
max_age = 15
for interval in (5, 10, 30):
    worst_age = source_delay + interval + processing + publication
    print(interval, worst_age, worst_age <= max_age)
assert source_delay + 5 + processing + publication == 13

Интервал пять минут даёт расчётную границу тринадцать минут, десять — восемнадцать, тридцать — тридцать восемь. Значит, для выбранного требования сначала имеет смысл проверить пятиминутный пакетный расчёт. Эти числа — входные допущения: в пилоте их заменяют измерениями, включая тяжёлые периоды. Если длительность обработки приближается к интервалу, отдельно проверяют накопление очереди и конкуренцию запусков.

Потоковый движок полезен, когда данные поступают постоянно и результат нужно обновлять по мере прихода событий. У Spark Structured Streaming есть потоковая модель вычислений, а распространённый режим выполняет последовательность небольших пакетов. Поэтому слово streaming само по себе ещё не отвечает, какой возраст результата увидит сотрудник.

Незавершённый день и окончательный итог

Быстрое обновление меняет договор с читателем таблицы. Продажа может прийти позже, возврат — завтра, справочник магазина — обновиться в середине дня. Решите, показываете ли вы предварительное число и когда оно становится закрытым итогом. Иначе два корректных обновления будут выглядеть как случайные расхождения, а сотрудники начнут сохранять собственные копии.

Для оперативной панели полезно хранить время последнего полученного события, время последнего успешного расчёта и отметку полноты периода. Один зелёный статус задания не доказывает, что источник передал всё. При задержке покажите возраст результата и заранее определённое действие: использовать предыдущую версию, приостановить автоматическое решение или обратиться к оператору.

У быстрого и закрытого результата могут быть разные маршруты. Например, оперативная таблица помогает выбрать полку для осмотра, а ночной пересчёт формирует окончательный отчёт. Объедините смысл метрик и ключи событий, но отдельно задайте правила исправлений. Тогда расхождение имеет объяснение: один результат ещё предварительный, другой уже учитывает поздние записи.

Как завершить выбор

В пилоте возьмите один потребительский сценарий, обычный день и период пиковой нагрузки. Измерьте возраст события в опубликованной таблице, число опоздавших решений, время восстановления после остановки источника и стоимость работы. Сравнивайте варианты при одинаковой полноте данных. Быстрая выдача половины событий не выполняет то же требование, что немного более поздний полный расчёт.

Решение удобно сохранить на одной странице: кому нужен результат, к какому сроку, какой путь проверили, на каких измерениях и что заставит пересмотреть архитектуру. Таким основанием может стать новый потребитель с минутным дедлайном или рост длительности расчёта. Само увеличение числа записей ещё не требует потокового режима: важны скорость поступления, сложность преобразований и срок действия результата.

Следующий шаг в практике

В Data Engineering Fast‑Track вы собираете локальный Kafka → Spark → Lakehouse под управлением Airflow и отрабатываете batch и streaming. Это позволяет перейти от расчёта допустимой свежести к рабочим способам доставки данных, настроить зависимости и проверить повторную загрузку. Откройте программу платного курса «Data Engineering Fast‑Track: Kafka → Airflow → Spark».

Связанный разбор: какой период считает ежедневный DAG.

Продолжить на практике

  • Data Engineering Fast‑Track: Kafka → Airflow → Spark ↗

    Практический fast-track по data engineering: за 4–6 недель собираем рабочий конвейер Kafka → Spark → Lakehouse под управлением Airflow. Разберём batch и streaming, окна и watermark, схемы и контракты. Мини‑проекты, Docker Compose, шаблоны DAG и пайплайнов. Курс подойдёт инженерам, аналитикам и тем, кто хочет освоить современные пайплайны данных с уверенным Python.