Une transformation décrit un calcul et ne l’exécute pas : elle renvoie un nouveau RDD ou DataFrame et Spark se contente de l’ajouter au plan. Une action déclenche l’exécution et renvoie un résultat au driver, ou écrit sur disque.
val filtre = df.filter($"age" > 18) // transformation — rien ne s'exécute
filtre.count() // action — le job part maintenantmap, filter, select, join, groupBy sont des transformations. count, collect, show, first, take, write, saveAsTextFile sont des actions.
Il ne teste pas votre mémoire des deux listes : il vérifie que vous savez pourquoi cette séparation existe. La bonne réponse mentionne l’optimisation.
Comme Spark attend l’action, il voit toute la chaîne d’un coup avant d’exécuter. Il peut alors fusionner les étapes, réordonner les filtres pour les appliquer au plus tôt, et pousser certains d’entre eux jusqu’à la source de données. Si chaque transformation s’exécutait immédiatement, aucune de ces réécritures ne serait possible.
C’est ce qu’on appelle l’évaluation paresseuse, et c’est la raison pour laquelle un job Spark de trente lignes ne fait rien jusqu’à la dernière.
« Pourquoi votre job semble-t-il instantané puis prend dix minutes ? »
Parce que les trente premières lignes n’étaient que des transformations, et que la ligne show() a déclenché l’ensemble. Le temps ne se lit pas là où le code se bloque.
« Et si vous appelez deux actions sur le même DataFrame ? »
Le plan est réexécuté entièrement à chaque action — Spark ne garde rien par défaut. Deux count() sur un DataFrame lu depuis S3 relisent S3 deux fois. C’est le moment où l’on parle de cache(), et c’est souvent la vraie question derrière celle-ci.
Le détail du mécanisme est développé dans pourquoi votre job ne fait rien avant une action.