Почему Spark работает быстро?

«Почему Spark работает быстро?» — вопрос из категории Apache Spark, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Ключевые причины:

  1. In-Memory Computing: Основной прирост скорости дает кэширование данных в оперативной памяти после первой операции с помощью persist() или cache(). Это позволяет избежать многократного чтения с диска в цепочке преобразований.

    val df = spark.read.parquet("hdfs://path/to/data")
    df.persist(StorageLevel.MEMORY_AND_DISK) // Кэшируем в памяти
    val aggregated = df.groupBy("category").sum("value") // Быстро, данные в RAM
  2. Lazy Evaluation (Отложенное выполнение): Spark строит Directed Acyclic Graph (DAG) операций, но не выполняет их до вызова action (например, collect(), count(), save()). Это позволяет Catalyst Optimizer анализировать весь план и применять оптимизации:

    • Predicate Pushdown: Фильтры переносятся как можно ближе к источнику данных.
    • Projection Pruning: Чтение только необходимых столбцов.
    • Constant Folding: Вычисление константных выражений на этапе компиляции.
  3. Параллелизм на уровне партиций: Данные автоматически разбиваются на партиции, которые обрабатываются параллельно на разных ядрах и узлах кластера. Управление размером партиций (spark.sql.shuffle.partitions) критично для производительности.

  4. Tungsten Engine (Phase 2): Это низкоуровневая оптимизация, которая:

    • Использует off-heap память для управления объектами Java, снижая накладные расходы сборщика мусора.
    • Применяет Whole-Stage Code Generation — компилирует целые этапы преобразований в один оптимизированный байт-код, минимизируя виртуальные вызовы.

Пример оптимизации Catalyst:

# Исходный запрос пользователя
result = spark.sql("""
    SELECT department, AVG(salary)
    FROM employees
    WHERE hire_date > '2020-01-01'
    GROUP BY department
""")
# Catalyst переместит фильтр (WHERE) до агрегации (GROUP BY),
# что сократит объем данных для обработки на раннем этапе.

На практике скорость достигается за счет правильного проектирования пайплайнов с учетом этих особенностей: кэширование промежуточных результатов, минимизация shuffle-операций и настройка партиционирования.