cache() est un raccourci pour persist() avec le niveau par défaut. persist(niveau) laisse choisir où et comment stocker. checkpoint() écrit sur un stockage fiable et coupe la lignée.
| Stockage | Lignée | Survit à l’application | |
|---|---|---|---|
cache() | mémoire (+ disque pour un DataFrame) | conservée | non |
persist(niveau) | au choix | conservée | non |
checkpoint() | HDFS / S3 | supprimée | oui |
Le niveau par défaut diffère selon l’API, et c’est un détail que peu de candidats connaissent : MEMORY_ONLY pour un RDD, MEMORY_AND_DISK pour un DataFrame. Un RDD mis en cache dont les partitions ne tiennent pas en mémoire est donc recalculé silencieusement, pas déversé sur disque.
Que vous savez quand ne pas cacher. C’est le vrai signal, parce que le cache mal placé est un anti-patron très répandu.
Le cache est utile quand un même DataFrame est réutilisé plusieurs fois, typiquement par plusieurs actions ou dans une boucle. Il est inutile — et nuisible — dans une chaîne linéaire lue une seule fois : il occupe de la mémoire qui manquera aux shuffles, ce qui peut ralentir le job qu’il devait accélérer.
Deux erreurs à mentionner :
unpersist oublié. Le cache reste jusqu’à la fin de l’application ou son éviction par l’algorithme LRU. Dans un job long, il faut libérer explicitement.df.cache() ne matérialise rien : c’est une transformation. Rien n’est stocké avant la première action.checkpoint fonctionneC’est la partie qui distingue la réponse d’un senior. Dans un traitement itératif — un algorithme de graphe, une boucle de machine learning — la lignée s’allonge à chaque itération. Au bout de quelques dizaines de tours, le plan devient si profond que le driver passe plus de temps à le manipuler qu’à calculer, et finit parfois en StackOverflowError.
Le cache ne résout pas ce problème : il stocke les données mais garde la lignée, puisqu’il doit pouvoir recalculer en cas d’éviction. Seul checkpoint() la tronque, en écrivant sur un stockage fiable dont Spark accepte de repartir.
sc.setCheckpointDir("hdfs:///tmp/checkpoints")
rdd.cache() // pour ne pas relire deux fois
rdd.checkpoint() // pour couper la lignéeLes deux se combinent : sans cache, le checkpoint déclenche un recalcul complet pour écrire.
Les niveaux de stockage détaillés sont dans cache, persist et checkpoint : quoi garder.