Apache Spark: impact du repartitionnement, du tri et de la mise en cache sur une jointure


10

J'explore le comportement de Spark en joignant une table à elle-même. J'utilise Databricks.

Mon scénario fictif est:

  1. Lire une table externe en tant que trame de données A (les fichiers sous-jacents sont au format delta)

  2. Définissez la trame de données B comme trame de données A avec seulement certaines colonnes sélectionnées

  3. Joignez les cadres de données A et B sur la colonne1 et la colonne2

(Oui, cela n'a pas beaucoup de sens, j'expérimente simplement pour comprendre la mécanique sous-jacente de Spark)

a = spark.read.table("table") \
.select("column1", "column2", "column3", "column4") \
.withColumn("columnA", lower((concat(col("column4"), lit("_"), col("column5")))))

b = a.select("column1", "column2", "columnA")

c= a.join(b, how="left", on = ["column1", "column2"])

Ma première tentative a été d'exécuter le code tel quel (tentative 1). J'ai ensuite essayé de répartir et de mettre en cache (tentative 2)

a = spark.read.table("table") \
.select("column1", "column2", "column3", "column4") \
.withColumn("columnA", lower((concat(col("column4"), lit("_"), col("column5")))))
.repartition(col("column1"), col("column2")).cache()

Enfin, j'ai repartitionné, trié et mis en cache

 a = spark.read.table("table") \
.select("column1", "column2", "column3", "column4") \
.withColumn("columnA", lower((concat(col("column4"), lit("_"), col("column5")))))
.repartition(col("column1"), col("column2")).sortWithinPartitions(col("column1"), col("column2")).cache()

Les dags respectifs générés sont tels que joints.

Mes questions sont:

  1. Pourquoi, dans la tentative 1, la table semble être mise en cache même si la mise en cache n'a pas été explicitement spécifiée.

  2. Pourquoi InMemoreTableScan est toujours suivi d'un autre nœud de ce type.

  3. Pourquoi, dans la tentative 3, la mise en cache semble avoir lieu en deux étapes?

  4. Pourquoi dans la tentative 3 WholeStageCodegen suit un (et un seul) InMemoreTableScan.

tentative 1

tentative 2

entrez la description de l'image ici


Je soupçonne que le lecteur DataFrame met en cache automatiquement les données lorsque la source est une table externe. J'ai une situation similaire où je lis des données à partir d'une table de base de données, alors qu'il est en cours de téléchargement, l'onglet "SQL" sur "Interface utilisateur détaillée" me montre le nombre de lignes en cours de téléchargement mais aucun fichier n'a encore été enregistré à l'emplacement spécifié . Je suppose qu'il connaît le nombre, car il met en cache les données quelque part et c'est ce qui apparaît sur le DAG. Si vous lisez les données d'un fichier texte localement, vous ne verrez pas l'état du cache.
Salim

Réponses:


4

Ce que vous observez dans ces 3 plans est un mélange de runtime DataBricks et de Spark.

Tout d'abord, lors de l'exécution de DataBricks runtime 3.3+, la mise en cache est automatiquement activée pour tous les fichiers parquet. Configuration correspondante pour cela: spark.databricks.io.cache.enabled true

Pour votre deuxième requête, InMemoryTableScan se produit deux fois car dès que la jointure a été appelée, spark a essayé de calculer le Dataset A et le Dataset B en parallèle. En supposant que différents exécuteurs ont reçu les tâches ci-dessus, les deux devront analyser la table à partir du cache (DataBricks).

Pour le troisième, InMemoryTableScan ne fait pas référence à la mise en cache en soi. Cela signifie simplement que quel que soit le catalyseur de plan formé, il fallait analyser la table en cache plusieurs fois.

PS: je ne peux pas visualiser le point 4 :)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.