Что такое lazy вычисления в Spark
Что проверяют
Понимаете ли вы разницу между преобразованием и действием и зачем Spark вообще откладывает вычисления. Для инженера данных за этим сразу идёт практика: где строится план, почему джоб полчаса ничего не делал, а потом упал, и когда нужен кэш.
Как отвечать
Преобразования (select, filter, join, withColumn) только строят план: возвращается новый DataFrame, данные не читаются. Вычисление запускает действие (count, collect, show, write). До этого момента Spark держит граф зависимостей.
Зачем так. Имея план целиком, Catalyst переставляет фильтры ближе к источнику, отбрасывает неиспользуемые колонки, проталкивает условия в формат файла, склеивает узкие преобразования в одну стадию без промежуточных записей. Пошаговое исполнение такой оптимизации не даёт.
Стадии режутся по шаффлам: цепочка узких операций идёт одним проходом, широкая операция (перераспределение, соединение, группировка) ставит границу.
Практический вывод: план выполняется заново на каждое действие. Два count по одному датафрейму прочитают источник дважды, поэтому переиспользуемый результат кэшируют (cache, persist) и потом освобождают. Посмотреть получившийся план помогают explain и вкладка SQL в веб-интерфейсе.
Где спотыкаются
Думают, что ленив весь интерфейс целиком. Чтение с выводом схемы (inferSchema для CSV и JSON) уже читает данные, явная схема убирает этот лишний проход.
Отлаживают не в том месте. Ошибка в выражении всплывает на строке с действием, а не там, где написано преобразование, и трассировка указывает на count или write. Помогает чтение плана, а не расстановка отладочных show, каждый из которых сам запускает полный расчёт.
Кэшируют всё подряд. Кэш это тоже стоимость: память исполнителей, вытеснение, сериализация. Он оправдан, когда датафрейм честно используется несколькими действиями или в итеративном алгоритме, и снимается сразу после.